1 | The following changes since commit b4fbe1f65a4769c09e6bf2d79fc84360f840f40e: | 1 | The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190129' into staging (2019-01-29 12:00:19 +0000) | 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://git.xanclic.moe/XanClic/qemu.git tags/pull-block-2019-01-31 | 7 | https://gitlab.com/stefanha/qemu.git tags/block-pull-request |
8 | 8 | ||
9 | for you to fetch changes up to 908b30164bbffad7430d551b2a03a8fbcaa631ef: | 9 | for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53: |
10 | 10 | ||
11 | iotests: Allow 147 to be run concurrently (2019-01-31 00:44:55 +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 | Block patches: | 14 | Pull request |
15 | - New debugging QMP command to explore block graphs | 15 | |
16 | - Converted DPRINTF()s to trace events | 16 | Add new thread-pool-min/thread-pool-max parameters to control the thread pool |
17 | - Fixed qemu-io's use of getopt() for systems with optreset | 17 | used for async I/O. |
18 | - Minor NVMe emulation fixes | ||
19 | - An iotest fix | ||
20 | 18 | ||
21 | ---------------------------------------------------------------- | 19 | ---------------------------------------------------------------- |
22 | Laurent Vivier (4): | ||
23 | block/ssh: Convert from DPRINTF() macro to trace events | ||
24 | block/curl: Convert from DPRINTF() macro to trace events | ||
25 | block/file-posix: Convert from DPRINTF() macro to trace events | ||
26 | block/sheepdog: Convert from DPRINTF() macro to trace events | ||
27 | 20 | ||
28 | Li Qiang (3): | 21 | Nicolas Saenz Julienne (3): |
29 | nvme: use TYPE_NVME instead of constant string | 22 | Introduce event-loop-base abstract class |
30 | nvme: ensure the num_queues is not zero | 23 | util/main-loop: Introduce the main loop into QOM |
31 | nvme: use pci_dev directly in nvme_realize | 24 | util/event-loop-base: Introduce options to set the thread pool size |
32 | 25 | ||
33 | Max Reitz (3): | 26 | qapi/qom.json | 43 ++++++++-- |
34 | iotests.py: Add qemu_nbd_pipe() | 27 | meson.build | 26 +++--- |
35 | iotests: Bind qemu-nbd to localhost in 147 | 28 | include/block/aio.h | 10 +++ |
36 | iotests: Allow 147 to be run concurrently | 29 | include/block/thread-pool.h | 3 + |
37 | 30 | include/qemu/main-loop.h | 10 +++ | |
38 | Richard W.M. Jones (1): | 31 | include/sysemu/event-loop-base.h | 41 +++++++++ |
39 | qemu-io: Add generic function for reinitializing optind. | 32 | include/sysemu/iothread.h | 6 +- |
40 | 33 | event-loop-base.c | 140 +++++++++++++++++++++++++++++++ | |
41 | Vladimir Sementsov-Ogievskiy (2): | 34 | iothread.c | 68 +++++---------- |
42 | qapi: add x-debug-query-block-graph | 35 | util/aio-posix.c | 1 + |
43 | scripts: add render_block_graph function for QEMUMachine | 36 | util/async.c | 20 +++++ |
44 | 37 | util/main-loop.c | 65 ++++++++++++++ | |
45 | configure | 14 ++++ | 38 | util/thread-pool.c | 55 +++++++++++- |
46 | qapi/block-core.json | 108 ++++++++++++++++++++++++ | 39 | 13 files changed, 419 insertions(+), 69 deletions(-) |
47 | include/block/block.h | 1 + | 40 | create mode 100644 include/sysemu/event-loop-base.h |
48 | include/qemu/osdep.h | 16 ++++ | 41 | create mode 100644 event-loop-base.c |
49 | include/sysemu/block-backend.h | 2 + | ||
50 | block.c | 148 +++++++++++++++++++++++++++++++++ | ||
51 | block/block-backend.c | 5 ++ | ||
52 | block/curl.c | 29 ++----- | ||
53 | block/file-posix.c | 25 ++---- | ||
54 | block/sheepdog.c | 47 ++++------- | ||
55 | block/ssh.c | 46 ++++------ | ||
56 | blockdev.c | 5 ++ | ||
57 | hw/block/nvme.c | 15 ++-- | ||
58 | qemu-img.c | 2 +- | ||
59 | qemu-io-cmds.c | 2 +- | ||
60 | block/trace-events | 47 +++++++++++ | ||
61 | scripts/render_block_graph.py | 120 ++++++++++++++++++++++++++ | ||
62 | tests/qemu-iotests/147 | 98 +++++++++++++++------- | ||
63 | tests/qemu-iotests/iotests.py | 14 ++++ | ||
64 | 19 files changed, 608 insertions(+), 136 deletions(-) | ||
65 | create mode 100755 scripts/render_block_graph.py | ||
66 | 42 | ||
67 | -- | 43 | -- |
68 | 2.20.1 | 44 | 2.35.1 |
69 | |||
70 | diff view generated by jsdifflib |
1 | From: Laurent Vivier <lvivier@redhat.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Laurent Vivier <lvivier@redhat.com> | 3 | Introduce the 'event-loop-base' abstract class, it'll hold the |
4 | Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | 4 | properties common to all event loops and provide the necessary hooks for |
5 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 5 | their creation and maintenance. Then have iothread inherit from it. |
6 | Message-id: 20181213162727.17438-2-lvivier@redhat.com | 6 | |
7 | [mreitz: Fixed type of ssh_{read,write}_return's parameter to be ssize_t | 7 | EventLoopBaseClass is defined as user creatable and provides a hook for |
8 | instead of size_t] | 8 | its children to attach themselves to the user creatable class 'complete' |
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 9 | function. It also provides an update_params() callback to propagate |
10 | property changes onto its children. | ||
11 | |||
12 | The new 'event-loop-base' class will live in the root directory. It is | ||
13 | built on its own using the 'link_whole' option (there are no direct | ||
14 | function dependencies between the class and its children, it all happens | ||
15 | trough 'constructor' magic). And also imposes new compilation | ||
16 | dependencies: | ||
17 | |||
18 | qom <- event-loop-base <- blockdev (iothread.c) | ||
19 | |||
20 | And in subsequent patches: | ||
21 | |||
22 | qom <- event-loop-base <- qemuutil (util/main-loop.c) | ||
23 | |||
24 | All this forced some amount of reordering in meson.build: | ||
25 | |||
26 | - Moved qom build definition before qemuutil. Doing it the other way | ||
27 | around (i.e. moving qemuutil after qom) isn't possible as a lot of | ||
28 | core libraries that live in between the two depend on it. | ||
29 | |||
30 | - Process the 'hw' subdir earlier, as it introduces files into the | ||
31 | 'qom' source set. | ||
32 | |||
33 | No functional changes intended. | ||
34 | |||
35 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
36 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
37 | Acked-by: Markus Armbruster <armbru@redhat.com> | ||
38 | Message-id: 20220425075723.20019-2-nsaenzju@redhat.com | ||
39 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
10 | --- | 40 | --- |
11 | block/ssh.c | 46 +++++++++++++++++----------------------------- | 41 | qapi/qom.json | 22 +++++-- |
12 | block/trace-events | 17 +++++++++++++++++ | 42 | meson.build | 23 ++++--- |
13 | 2 files changed, 34 insertions(+), 29 deletions(-) | 43 | include/sysemu/event-loop-base.h | 36 +++++++++++ |
14 | 44 | include/sysemu/iothread.h | 6 +- | |
15 | diff --git a/block/ssh.c b/block/ssh.c | 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 | ||
16 | index XXXXXXX..XXXXXXX 100644 | 52 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/block/ssh.c | 53 | --- a/qapi/qom.json |
18 | +++ b/block/ssh.c | 54 | +++ b/qapi/qom.json |
19 | @@ -XXX,XX +XXX,XX @@ | 55 | @@ -XXX,XX +XXX,XX @@ |
20 | #include "qapi/qmp/qstring.h" | 56 | '*repeat': 'bool', |
21 | #include "qapi/qobject-input-visitor.h" | 57 | '*grab-toggle': 'GrabToggleKeys' } } |
22 | #include "qapi/qobject-output-visitor.h" | 58 | |
23 | +#include "trace.h" | 59 | +## |
24 | 60 | +# @EventLoopBaseProperties: | |
25 | -/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in | 61 | +# |
26 | - * this block driver code. | 62 | +# Common properties for event loops |
27 | - * | 63 | +# |
64 | +# @aio-max-batch: maximum number of requests in a batch for the AIO engine, | ||
65 | +# 0 means that the engine will use its default. | ||
66 | +# (default: 0) | ||
67 | +# | ||
68 | +# Since: 7.1 | ||
69 | +## | ||
70 | +{ 'struct': 'EventLoopBaseProperties', | ||
71 | + 'data': { '*aio-max-batch': 'int' } } | ||
72 | + | ||
73 | ## | ||
74 | # @IothreadProperties: | ||
75 | # | ||
76 | @@ -XXX,XX +XXX,XX @@ | ||
77 | # algorithm detects it is spending too long polling without | ||
78 | # encountering events. 0 selects a default behaviour (default: 0) | ||
79 | # | ||
80 | -# @aio-max-batch: maximum number of requests in a batch for the AIO engine, | ||
81 | -# 0 means that the engine will use its default | ||
82 | -# (default:0, since 6.1) | ||
83 | +# The @aio-max-batch option is available since 6.1. | ||
84 | # | ||
85 | # Since: 2.0 | ||
86 | ## | ||
87 | { 'struct': 'IothreadProperties', | ||
88 | + 'base': 'EventLoopBaseProperties', | ||
89 | 'data': { '*poll-max-ns': 'int', | ||
90 | '*poll-grow': 'int', | ||
91 | - '*poll-shrink': 'int', | ||
92 | - '*aio-max-batch': 'int' } } | ||
93 | + '*poll-shrink': 'int' } } | ||
94 | |||
95 | ## | ||
96 | # @MemoryBackendProperties: | ||
97 | diff --git a/meson.build b/meson.build | ||
98 | index XXXXXXX..XXXXXXX 100644 | ||
99 | --- a/meson.build | ||
100 | +++ b/meson.build | ||
101 | @@ -XXX,XX +XXX,XX @@ subdir('qom') | ||
102 | subdir('authz') | ||
103 | subdir('crypto') | ||
104 | subdir('ui') | ||
105 | +subdir('hw') | ||
106 | |||
107 | |||
108 | if enable_modules | ||
109 | @@ -XXX,XX +XXX,XX @@ if enable_modules | ||
110 | modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO') | ||
111 | endif | ||
112 | |||
113 | +qom_ss = qom_ss.apply(config_host, strict: false) | ||
114 | +libqom = static_library('qom', qom_ss.sources() + genh, | ||
115 | + dependencies: [qom_ss.dependencies()], | ||
116 | + name_suffix: 'fa') | ||
117 | +qom = declare_dependency(link_whole: libqom) | ||
118 | + | ||
119 | +event_loop_base = files('event-loop-base.c') | ||
120 | +event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh, | ||
121 | + build_by_default: true) | ||
122 | +event_loop_base = declare_dependency(link_whole: event_loop_base, | ||
123 | + dependencies: [qom]) | ||
124 | + | ||
125 | stub_ss = stub_ss.apply(config_all, strict: false) | ||
126 | |||
127 | util_ss.add_all(trace_ss) | ||
128 | @@ -XXX,XX +XXX,XX @@ subdir('monitor') | ||
129 | subdir('net') | ||
130 | subdir('replay') | ||
131 | subdir('semihosting') | ||
132 | -subdir('hw') | ||
133 | subdir('tcg') | ||
134 | subdir('fpu') | ||
135 | subdir('accel') | ||
136 | @@ -XXX,XX +XXX,XX @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms', | ||
137 | capture: true, | ||
138 | command: [undefsym, nm, '@INPUT@']) | ||
139 | |||
140 | -qom_ss = qom_ss.apply(config_host, strict: false) | ||
141 | -libqom = static_library('qom', qom_ss.sources() + genh, | ||
142 | - dependencies: [qom_ss.dependencies()], | ||
143 | - name_suffix: 'fa') | ||
144 | - | ||
145 | -qom = declare_dependency(link_whole: libqom) | ||
146 | - | ||
147 | authz_ss = authz_ss.apply(config_host, strict: false) | ||
148 | libauthz = static_library('authz', authz_ss.sources() + genh, | ||
149 | dependencies: [authz_ss.dependencies()], | ||
150 | @@ -XXX,XX +XXX,XX @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, | ||
151 | build_by_default: false) | ||
152 | |||
153 | blockdev = declare_dependency(link_whole: [libblockdev], | ||
154 | - dependencies: [block]) | ||
155 | + dependencies: [block, event_loop_base]) | ||
156 | |||
157 | qmp_ss = qmp_ss.apply(config_host, strict: false) | ||
158 | libqmp = static_library('qmp', qmp_ss.sources() + genh, | ||
159 | diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h | ||
160 | new file mode 100644 | ||
161 | index XXXXXXX..XXXXXXX | ||
162 | --- /dev/null | ||
163 | +++ b/include/sysemu/event-loop-base.h | ||
164 | @@ -XXX,XX +XXX,XX @@ | ||
28 | +/* | 165 | +/* |
29 | * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note | 166 | + * QEMU event-loop backend |
30 | * that this requires that libssh2 was specially compiled with the | 167 | + * |
31 | * `./configure --enable-debug' option, so most likely you will have | 168 | + * Copyright (C) 2022 Red Hat Inc |
32 | * to compile it yourself. The meaning of <bitmask> is described | 169 | + * |
33 | * here: http://www.libssh2.org/libssh2_trace.html | 170 | + * Authors: |
34 | */ | 171 | + * Nicolas Saenz Julienne <nsaenzju@redhat.com> |
35 | -#define DEBUG_SSH 0 | 172 | + * |
36 | #define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ | 173 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. |
37 | 174 | + * See the COPYING file in the top-level directory. | |
38 | -#define DPRINTF(fmt, ...) \ | 175 | + */ |
39 | - do { \ | 176 | +#ifndef QEMU_EVENT_LOOP_BASE_H |
40 | - if (DEBUG_SSH) { \ | 177 | +#define QEMU_EVENT_LOOP_BASE_H |
41 | - fprintf(stderr, "ssh: %-15s " fmt "\n", \ | 178 | + |
42 | - __func__, ##__VA_ARGS__); \ | 179 | +#include "qom/object.h" |
43 | - } \ | 180 | +#include "block/aio.h" |
44 | - } while (0) | 181 | +#include "qemu/typedefs.h" |
45 | - | 182 | + |
46 | typedef struct BDRVSSHState { | 183 | +#define TYPE_EVENT_LOOP_BASE "event-loop-base" |
47 | /* Coroutine. */ | 184 | +OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass, |
48 | CoMutex lock; | 185 | + EVENT_LOOP_BASE) |
49 | @@ -XXX,XX +XXX,XX @@ static int check_host_key_knownhosts(BDRVSSHState *s, | 186 | + |
50 | switch (r) { | 187 | +struct EventLoopBaseClass { |
51 | case LIBSSH2_KNOWNHOST_CHECK_MATCH: | 188 | + ObjectClass parent_class; |
52 | /* OK */ | 189 | + |
53 | - DPRINTF("host key OK: %s", found->key); | 190 | + void (*init)(EventLoopBase *base, Error **errp); |
54 | + trace_ssh_check_host_key_knownhosts(found->key); | 191 | + void (*update_params)(EventLoopBase *base, Error **errp); |
55 | break; | 192 | +}; |
56 | case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: | 193 | + |
57 | ret = -EINVAL; | 194 | +struct EventLoopBase { |
58 | @@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, | 195 | + Object parent; |
196 | + | ||
197 | + /* AioContext AIO engine parameters */ | ||
198 | + int64_t aio_max_batch; | ||
199 | +}; | ||
200 | +#endif | ||
201 | diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h | ||
202 | index XXXXXXX..XXXXXXX 100644 | ||
203 | --- a/include/sysemu/iothread.h | ||
204 | +++ b/include/sysemu/iothread.h | ||
205 | @@ -XXX,XX +XXX,XX @@ | ||
206 | #include "block/aio.h" | ||
207 | #include "qemu/thread.h" | ||
208 | #include "qom/object.h" | ||
209 | +#include "sysemu/event-loop-base.h" | ||
210 | |||
211 | #define TYPE_IOTHREAD "iothread" | ||
212 | |||
213 | struct IOThread { | ||
214 | - Object parent_obj; | ||
215 | + EventLoopBase parent_obj; | ||
216 | |||
217 | QemuThread thread; | ||
218 | AioContext *ctx; | ||
219 | @@ -XXX,XX +XXX,XX @@ struct IOThread { | ||
220 | int64_t poll_max_ns; | ||
221 | int64_t poll_grow; | ||
222 | int64_t poll_shrink; | ||
223 | - | ||
224 | - /* AioContext AIO engine parameters */ | ||
225 | - int64_t aio_max_batch; | ||
226 | }; | ||
227 | typedef struct IOThread IOThread; | ||
228 | |||
229 | diff --git a/event-loop-base.c b/event-loop-base.c | ||
230 | new file mode 100644 | ||
231 | index XXXXXXX..XXXXXXX | ||
232 | --- /dev/null | ||
233 | +++ b/event-loop-base.c | ||
234 | @@ -XXX,XX +XXX,XX @@ | ||
235 | +/* | ||
236 | + * QEMU event-loop base | ||
237 | + * | ||
238 | + * Copyright (C) 2022 Red Hat Inc | ||
239 | + * | ||
240 | + * Authors: | ||
241 | + * Stefan Hajnoczi <stefanha@redhat.com> | ||
242 | + * Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
243 | + * | ||
244 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
245 | + * See the COPYING file in the top-level directory. | ||
246 | + */ | ||
247 | + | ||
248 | +#include "qemu/osdep.h" | ||
249 | +#include "qom/object_interfaces.h" | ||
250 | +#include "qapi/error.h" | ||
251 | +#include "sysemu/event-loop-base.h" | ||
252 | + | ||
253 | +typedef struct { | ||
254 | + const char *name; | ||
255 | + ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */ | ||
256 | +} EventLoopBaseParamInfo; | ||
257 | + | ||
258 | +static EventLoopBaseParamInfo aio_max_batch_info = { | ||
259 | + "aio-max-batch", offsetof(EventLoopBase, aio_max_batch), | ||
260 | +}; | ||
261 | + | ||
262 | +static void event_loop_base_get_param(Object *obj, Visitor *v, | ||
263 | + const char *name, void *opaque, Error **errp) | ||
264 | +{ | ||
265 | + EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj); | ||
266 | + EventLoopBaseParamInfo *info = opaque; | ||
267 | + int64_t *field = (void *)event_loop_base + info->offset; | ||
268 | + | ||
269 | + visit_type_int64(v, name, field, errp); | ||
270 | +} | ||
271 | + | ||
272 | +static void event_loop_base_set_param(Object *obj, Visitor *v, | ||
273 | + const char *name, void *opaque, Error **errp) | ||
274 | +{ | ||
275 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj); | ||
276 | + EventLoopBase *base = EVENT_LOOP_BASE(obj); | ||
277 | + EventLoopBaseParamInfo *info = opaque; | ||
278 | + int64_t *field = (void *)base + info->offset; | ||
279 | + int64_t value; | ||
280 | + | ||
281 | + if (!visit_type_int64(v, name, &value, errp)) { | ||
282 | + return; | ||
283 | + } | ||
284 | + | ||
285 | + if (value < 0) { | ||
286 | + error_setg(errp, "%s value must be in range [0, %" PRId64 "]", | ||
287 | + info->name, INT64_MAX); | ||
288 | + return; | ||
289 | + } | ||
290 | + | ||
291 | + *field = value; | ||
292 | + | ||
293 | + if (bc->update_params) { | ||
294 | + bc->update_params(base, errp); | ||
295 | + } | ||
296 | + | ||
297 | + return; | ||
298 | +} | ||
299 | + | ||
300 | +static void event_loop_base_complete(UserCreatable *uc, Error **errp) | ||
301 | +{ | ||
302 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc); | ||
303 | + EventLoopBase *base = EVENT_LOOP_BASE(uc); | ||
304 | + | ||
305 | + if (bc->init) { | ||
306 | + bc->init(base, errp); | ||
307 | + } | ||
308 | +} | ||
309 | + | ||
310 | +static void event_loop_base_class_init(ObjectClass *klass, void *class_data) | ||
311 | +{ | ||
312 | + UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); | ||
313 | + ucc->complete = event_loop_base_complete; | ||
314 | + | ||
315 | + object_class_property_add(klass, "aio-max-batch", "int", | ||
316 | + event_loop_base_get_param, | ||
317 | + event_loop_base_set_param, | ||
318 | + NULL, &aio_max_batch_info); | ||
319 | +} | ||
320 | + | ||
321 | +static const TypeInfo event_loop_base_info = { | ||
322 | + .name = TYPE_EVENT_LOOP_BASE, | ||
323 | + .parent = TYPE_OBJECT, | ||
324 | + .instance_size = sizeof(EventLoopBase), | ||
325 | + .class_size = sizeof(EventLoopBaseClass), | ||
326 | + .class_init = event_loop_base_class_init, | ||
327 | + .abstract = true, | ||
328 | + .interfaces = (InterfaceInfo[]) { | ||
329 | + { TYPE_USER_CREATABLE }, | ||
330 | + { } | ||
331 | + } | ||
332 | +}; | ||
333 | + | ||
334 | +static void register_types(void) | ||
335 | +{ | ||
336 | + type_register_static(&event_loop_base_info); | ||
337 | +} | ||
338 | +type_init(register_types); | ||
339 | diff --git a/iothread.c b/iothread.c | ||
340 | index XXXXXXX..XXXXXXX 100644 | ||
341 | --- a/iothread.c | ||
342 | +++ b/iothread.c | ||
343 | @@ -XXX,XX +XXX,XX @@ | ||
344 | #include "qemu/module.h" | ||
345 | #include "block/aio.h" | ||
346 | #include "block/block.h" | ||
347 | +#include "sysemu/event-loop-base.h" | ||
348 | #include "sysemu/iothread.h" | ||
349 | #include "qapi/error.h" | ||
350 | #include "qapi/qapi-commands-misc.h" | ||
351 | @@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread) | ||
352 | iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); | ||
353 | } | ||
354 | |||
355 | -static void iothread_set_aio_context_params(IOThread *iothread, Error **errp) | ||
356 | +static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) | ||
357 | { | ||
358 | + IOThread *iothread = IOTHREAD(base); | ||
359 | ERRP_GUARD(); | ||
360 | |||
361 | + if (!iothread->ctx) { | ||
362 | + return; | ||
363 | + } | ||
364 | + | ||
365 | aio_context_set_poll_params(iothread->ctx, | ||
366 | iothread->poll_max_ns, | ||
367 | iothread->poll_grow, | ||
368 | @@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp) | ||
59 | } | 369 | } |
60 | 370 | ||
61 | /* Open the remote file. */ | 371 | aio_context_set_aio_params(iothread->ctx, |
62 | - DPRINTF("opening file %s flags=0x%x creat_mode=0%o", | 372 | - iothread->aio_max_batch, |
63 | - opts->path, ssh_flags, creat_mode); | 373 | + iothread->parent_obj.aio_max_batch, |
64 | + trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode); | 374 | errp); |
65 | s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags, | 375 | } |
66 | creat_mode); | 376 | |
67 | if (!s->sftp_handle) { | 377 | -static void iothread_complete(UserCreatable *obj, Error **errp) |
68 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, | 378 | + |
69 | /* Get desired file size. */ | 379 | +static void iothread_init(EventLoopBase *base, Error **errp) |
70 | ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), | 380 | { |
71 | BDRV_SECTOR_SIZE); | 381 | Error *local_error = NULL; |
72 | - DPRINTF("total_size=%" PRIi64, ssh_opts->size); | 382 | - IOThread *iothread = IOTHREAD(obj); |
73 | + trace_ssh_co_create_opts(ssh_opts->size); | 383 | + IOThread *iothread = IOTHREAD(base); |
74 | 384 | char *thread_name; | |
75 | uri_options = qdict_new(); | 385 | |
76 | ret = parse_uri(filename, uri_options, errp); | 386 | iothread->stopping = false; |
77 | @@ -XXX,XX +XXX,XX @@ static void restart_coroutine(void *opaque) | 387 | @@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp) |
78 | BDRVSSHState *s = bs->opaque; | 388 | */ |
79 | AioContext *ctx = bdrv_get_aio_context(bs); | 389 | iothread_init_gcontext(iothread); |
80 | 390 | ||
81 | - DPRINTF("co=%p", restart->co); | 391 | - iothread_set_aio_context_params(iothread, &local_error); |
82 | + trace_ssh_restart_coroutine(restart->co); | 392 | + iothread_set_aio_context_params(base, &local_error); |
83 | aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL); | 393 | if (local_error) { |
84 | 394 | error_propagate(errp, local_error); | |
85 | aio_co_wake(restart->co); | 395 | aio_context_unref(iothread->ctx); |
86 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) | 396 | @@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp) |
87 | wr_handler = restart_coroutine; | 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, | ||
88 | } | 416 | } |
89 | |||
90 | - DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, | ||
91 | - rd_handler, wr_handler); | ||
92 | + trace_ssh_co_yield(s->sock, rd_handler, wr_handler); | ||
93 | |||
94 | aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, | ||
95 | false, rd_handler, wr_handler, NULL, &restart); | ||
96 | qemu_coroutine_yield(); | ||
97 | - DPRINTF("s->sock=%d - back", s->sock); | ||
98 | + trace_ssh_co_yield_back(s->sock); | ||
99 | } | 417 | } |
100 | 418 | ||
101 | /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position | 419 | -static void iothread_get_aio_param(Object *obj, Visitor *v, |
102 | @@ -XXX,XX +XXX,XX @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) | 420 | - const char *name, void *opaque, Error **errp) |
103 | bool force = (flags & SSH_SEEK_FORCE) != 0; | 421 | -{ |
104 | 422 | - IOThreadParamInfo *info = opaque; | |
105 | if (force || op_read != s->offset_op_read || offset != s->offset) { | 423 | - |
106 | - DPRINTF("seeking to offset=%" PRIi64, offset); | 424 | - iothread_get_param(obj, v, name, info, errp); |
107 | + trace_ssh_seek(offset); | 425 | -} |
108 | libssh2_sftp_seek64(s->sftp_handle, offset); | 426 | - |
109 | s->offset = offset; | 427 | -static void iothread_set_aio_param(Object *obj, Visitor *v, |
110 | s->offset_op_read = op_read; | 428 | - const char *name, void *opaque, Error **errp) |
111 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, | 429 | -{ |
112 | char *buf, *end_of_vec; | 430 | - IOThread *iothread = IOTHREAD(obj); |
113 | struct iovec *i; | 431 | - IOThreadParamInfo *info = opaque; |
114 | 432 | - | |
115 | - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); | 433 | - if (!iothread_set_param(obj, v, name, info, errp)) { |
116 | + trace_ssh_read(offset, size); | 434 | - return; |
117 | 435 | - } | |
118 | ssh_seek(s, offset, SSH_SEEK_READ); | 436 | - |
119 | 437 | - if (iothread->ctx) { | |
120 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, | 438 | - aio_context_set_aio_params(iothread->ctx, |
121 | */ | 439 | - iothread->aio_max_batch, |
122 | for (got = 0; got < size; ) { | 440 | - errp); |
123 | again: | 441 | - } |
124 | - DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); | 442 | -} |
125 | + trace_ssh_read_buf(buf, end_of_vec - buf); | 443 | - |
126 | r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); | 444 | static void iothread_class_init(ObjectClass *klass, void *class_data) |
127 | - DPRINTF("sftp_read returned %zd", r); | ||
128 | + trace_ssh_read_return(r); | ||
129 | |||
130 | if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | ||
131 | co_yield(s, bs); | ||
132 | @@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, | ||
133 | char *buf, *end_of_vec; | ||
134 | struct iovec *i; | ||
135 | |||
136 | - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); | ||
137 | + trace_ssh_write(offset, size); | ||
138 | |||
139 | ssh_seek(s, offset, SSH_SEEK_WRITE); | ||
140 | |||
141 | @@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, | ||
142 | |||
143 | for (written = 0; written < size; ) { | ||
144 | again: | ||
145 | - DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); | ||
146 | + trace_ssh_write_buf(buf, end_of_vec - buf); | ||
147 | r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); | ||
148 | - DPRINTF("sftp_write returned %zd", r); | ||
149 | + trace_ssh_write_return(r); | ||
150 | |||
151 | if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | ||
152 | co_yield(s, bs); | ||
153 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) | ||
154 | { | 445 | { |
155 | int r; | 446 | - UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); |
156 | 447 | - ucc->complete = iothread_complete; | |
157 | - DPRINTF("fsync"); | 448 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass); |
158 | + trace_ssh_flush(); | 449 | + |
159 | again: | 450 | + bc->init = iothread_init; |
160 | r = libssh2_sftp_fsync(s->sftp_handle); | 451 | + bc->update_params = iothread_set_aio_context_params; |
161 | if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { | 452 | |
162 | @@ -XXX,XX +XXX,XX @@ static int64_t ssh_getlength(BlockDriverState *bs) | 453 | object_class_property_add(klass, "poll-max-ns", "int", |
163 | 454 | iothread_get_poll_param, | |
164 | /* Note we cannot make a libssh2 call here. */ | 455 | @@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data) |
165 | length = (int64_t) s->attrs.filesize; | 456 | iothread_get_poll_param, |
166 | - DPRINTF("length=%" PRIi64, length); | 457 | iothread_set_poll_param, |
167 | + trace_ssh_getlength(length); | 458 | NULL, &poll_shrink_info); |
168 | 459 | - object_class_property_add(klass, "aio-max-batch", "int", | |
169 | return length; | 460 | - iothread_get_aio_param, |
461 | - iothread_set_aio_param, | ||
462 | - NULL, &aio_max_batch_info); | ||
170 | } | 463 | } |
171 | diff --git a/block/trace-events b/block/trace-events | 464 | |
172 | index XXXXXXX..XXXXXXX 100644 | 465 | static const TypeInfo iothread_info = { |
173 | --- a/block/trace-events | 466 | .name = TYPE_IOTHREAD, |
174 | +++ b/block/trace-events | 467 | - .parent = TYPE_OBJECT, |
175 | @@ -XXX,XX +XXX,XX @@ iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, ui | 468 | + .parent = TYPE_EVENT_LOOP_BASE, |
176 | # block/nbd-client.c | 469 | .class_init = iothread_class_init, |
177 | nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" | 470 | .instance_size = sizeof(IOThread), |
178 | nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" | 471 | .instance_init = iothread_instance_init, |
179 | + | 472 | .instance_finalize = iothread_instance_finalize, |
180 | +# block/ssh.c | 473 | - .interfaces = (InterfaceInfo[]) { |
181 | +ssh_restart_coroutine(void *co) "co=%p" | 474 | - {TYPE_USER_CREATABLE}, |
182 | +ssh_flush(void) "fsync" | 475 | - {} |
183 | +ssh_check_host_key_knownhosts(const char *key) "host key OK: %s" | 476 | - }, |
184 | +ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o" | 477 | }; |
185 | +ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p" | 478 | |
186 | +ssh_co_yield_back(int sock) "s->sock=%d - back" | 479 | static void iothread_register_types(void) |
187 | +ssh_getlength(int64_t length) "length=%" PRIi64 | 480 | @@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque) |
188 | +ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64 | 481 | info->poll_max_ns = iothread->poll_max_ns; |
189 | +ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" | 482 | info->poll_grow = iothread->poll_grow; |
190 | +ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu" | 483 | info->poll_shrink = iothread->poll_shrink; |
191 | +ssh_read_return(ssize_t ret) "sftp_read returned %zd" | 484 | - info->aio_max_batch = iothread->aio_max_batch; |
192 | +ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" | 485 | + info->aio_max_batch = iothread->parent_obj.aio_max_batch; |
193 | +ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu" | 486 | |
194 | +ssh_write_return(ssize_t ret) "sftp_write returned %zd" | 487 | QAPI_LIST_APPEND(*tail, info); |
195 | +ssh_seek(int64_t offset) "seeking to offset=%" PRIi64 | 488 | return 0; |
196 | -- | 489 | -- |
197 | 2.20.1 | 490 | 2.35.1 |
198 | |||
199 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Add a new command, returning block nodes (and their users) graph. | 3 | 'event-loop-base' provides basic property handling for all 'AioContext' |
4 | 4 | based event loops. So let's define a new 'MainLoopClass' that inherits | |
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 5 | from it. This will permit tweaking the main loop's properties through |
6 | Message-id: 20181221170909.25584-2-vsementsov@virtuozzo.com | 6 | qapi as well as through the command line using the '-object' keyword[1]. |
7 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 7 | Only one instance of 'MainLoopClass' might be created at any time. |
8 | |||
9 | 'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to | ||
10 | mark 'MainLoop' as non-deletable. | ||
11 | |||
12 | [1] For example: | ||
13 | -object main-loop,id=main-loop,aio-max-batch=<value> | ||
14 | |||
15 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
16 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
17 | Acked-by: Markus Armbruster <armbru@redhat.com> | ||
18 | Message-id: 20220425075723.20019-3-nsaenzju@redhat.com | ||
19 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
8 | --- | 20 | --- |
9 | qapi/block-core.json | 108 ++++++++++++++++++++++++ | 21 | qapi/qom.json | 13 ++++++++ |
10 | include/block/block.h | 1 + | 22 | meson.build | 3 +- |
11 | include/sysemu/block-backend.h | 2 + | 23 | include/qemu/main-loop.h | 10 ++++++ |
12 | block.c | 148 +++++++++++++++++++++++++++++++++ | 24 | include/sysemu/event-loop-base.h | 1 + |
13 | block/block-backend.c | 5 ++ | 25 | event-loop-base.c | 13 ++++++++ |
14 | blockdev.c | 5 ++ | 26 | util/main-loop.c | 56 ++++++++++++++++++++++++++++++++ |
15 | 6 files changed, 269 insertions(+) | 27 | 6 files changed, 95 insertions(+), 1 deletion(-) |
16 | 28 | ||
17 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 29 | diff --git a/qapi/qom.json b/qapi/qom.json |
18 | index XXXXXXX..XXXXXXX 100644 | 30 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/qapi/block-core.json | 31 | --- a/qapi/qom.json |
20 | +++ b/qapi/block-core.json | 32 | +++ b/qapi/qom.json |
21 | @@ -XXX,XX +XXX,XX @@ | 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 | + | ||
22 | ## | 48 | ## |
23 | { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] } | 49 | # @MemoryBackendProperties: |
24 | |||
25 | +## | ||
26 | +# @XDbgBlockGraphNodeType: | ||
27 | +# | ||
28 | +# @block-backend: corresponds to BlockBackend | ||
29 | +# | ||
30 | +# @block-job: corresonds to BlockJob | ||
31 | +# | ||
32 | +# @block-driver: corresponds to BlockDriverState | ||
33 | +# | ||
34 | +# Since: 4.0 | ||
35 | +## | ||
36 | +{ 'enum': 'XDbgBlockGraphNodeType', | ||
37 | + 'data': [ 'block-backend', 'block-job', 'block-driver' ] } | ||
38 | + | ||
39 | +## | ||
40 | +# @XDbgBlockGraphNode: | ||
41 | +# | ||
42 | +# @id: Block graph node identifier. This @id is generated only for | ||
43 | +# x-debug-query-block-graph and does not relate to any other identifiers in | ||
44 | +# Qemu. | ||
45 | +# | ||
46 | +# @type: Type of graph node. Can be one of block-backend, block-job or | ||
47 | +# block-driver-state. | ||
48 | +# | ||
49 | +# @name: Human readable name of the node. Corresponds to node-name for | ||
50 | +# block-driver-state nodes; is not guaranteed to be unique in the whole | ||
51 | +# graph (with block-jobs and block-backends). | ||
52 | +# | ||
53 | +# Since: 4.0 | ||
54 | +## | ||
55 | +{ 'struct': 'XDbgBlockGraphNode', | ||
56 | + 'data': { 'id': 'uint64', 'type': 'XDbgBlockGraphNodeType', 'name': 'str' } } | ||
57 | + | ||
58 | +## | ||
59 | +# @BlockPermission: | ||
60 | +# | ||
61 | +# Enum of base block permissions. | ||
62 | +# | ||
63 | +# @consistent-read: A user that has the "permission" of consistent reads is | ||
64 | +# guaranteed that their view of the contents of the block | ||
65 | +# device is complete and self-consistent, representing the | ||
66 | +# contents of a disk at a specific point. | ||
67 | +# For most block devices (including their backing files) this | ||
68 | +# is true, but the property cannot be maintained in a few | ||
69 | +# situations like for intermediate nodes of a commit block | ||
70 | +# job. | ||
71 | +# | ||
72 | +# @write: This permission is required to change the visible disk contents. | ||
73 | +# | ||
74 | +# @write-unchanged: This permission (which is weaker than BLK_PERM_WRITE) is | ||
75 | +# both enough and required for writes to the block node when | ||
76 | +# the caller promises that the visible disk content doesn't | ||
77 | +# change. | ||
78 | +# As the BLK_PERM_WRITE permission is strictly stronger, | ||
79 | +# either is sufficient to perform an unchanging write. | ||
80 | +# | ||
81 | +# @resize: This permission is required to change the size of a block node. | ||
82 | +# | ||
83 | +# @graph-mod: This permission is required to change the node that this | ||
84 | +# BdrvChild points to. | ||
85 | +# | ||
86 | +# Since: 4.0 | ||
87 | +## | ||
88 | + { 'enum': 'BlockPermission', | ||
89 | + 'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize', | ||
90 | + 'graph-mod' ] } | ||
91 | +## | ||
92 | +# @XDbgBlockGraphEdge: | ||
93 | +# | ||
94 | +# Block Graph edge description for x-debug-query-block-graph. | ||
95 | +# | ||
96 | +# @parent: parent id | ||
97 | +# | ||
98 | +# @child: child id | ||
99 | +# | ||
100 | +# @name: name of the relation (examples are 'file' and 'backing') | ||
101 | +# | ||
102 | +# @perm: granted permissions for the parent operating on the child | ||
103 | +# | ||
104 | +# @shared-perm: permissions that can still be granted to other users of the | ||
105 | +# child while it is still attached to this parent | ||
106 | +# | ||
107 | +# Since: 4.0 | ||
108 | +## | ||
109 | +{ 'struct': 'XDbgBlockGraphEdge', | ||
110 | + 'data': { 'parent': 'uint64', 'child': 'uint64', | ||
111 | + 'name': 'str', 'perm': [ 'BlockPermission' ], | ||
112 | + 'shared-perm': [ 'BlockPermission' ] } } | ||
113 | + | ||
114 | +## | ||
115 | +# @XDbgBlockGraph: | ||
116 | +# | ||
117 | +# Block Graph - list of nodes and list of edges. | ||
118 | +# | ||
119 | +# Since: 4.0 | ||
120 | +## | ||
121 | +{ 'struct': 'XDbgBlockGraph', | ||
122 | + 'data': { 'nodes': ['XDbgBlockGraphNode'], 'edges': ['XDbgBlockGraphEdge'] } } | ||
123 | + | ||
124 | +## | ||
125 | +# @x-debug-query-block-graph: | ||
126 | +# | ||
127 | +# Get the block graph. | ||
128 | +# | ||
129 | +# Since: 4.0 | ||
130 | +## | ||
131 | +{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph' } | ||
132 | + | ||
133 | ## | ||
134 | # @drive-mirror: | ||
135 | # | 50 | # |
136 | diff --git a/include/block/block.h b/include/block/block.h | 51 | @@ -XXX,XX +XXX,XX @@ |
137 | index XXXXXXX..XXXXXXX 100644 | 52 | { 'name': 'input-linux', |
138 | --- a/include/block/block.h | 53 | 'if': 'CONFIG_LINUX' }, |
139 | +++ b/include/block/block.h | 54 | 'iothread', |
140 | @@ -XXX,XX +XXX,XX @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag); | 55 | + 'main-loop', |
141 | const char *bdrv_get_format_name(BlockDriverState *bs); | 56 | { 'name': 'memory-backend-epc', |
142 | BlockDriverState *bdrv_find_node(const char *node_name); | 57 | 'if': 'CONFIG_LINUX' }, |
143 | BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp); | 58 | 'memory-backend-file', |
144 | +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); | 59 | @@ -XXX,XX +XXX,XX @@ |
145 | BlockDriverState *bdrv_lookup_bs(const char *device, | 60 | 'input-linux': { 'type': 'InputLinuxProperties', |
146 | const char *node_name, | 61 | 'if': 'CONFIG_LINUX' }, |
147 | Error **errp); | 62 | 'iothread': 'IothreadProperties', |
148 | diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h | 63 | + 'main-loop': 'MainLoopProperties', |
149 | index XXXXXXX..XXXXXXX 100644 | 64 | 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', |
150 | --- a/include/sysemu/block-backend.h | 65 | 'if': 'CONFIG_LINUX' }, |
151 | +++ b/include/sysemu/block-backend.h | 66 | 'memory-backend-file': 'MemoryBackendFileProperties', |
152 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, | 67 | diff --git a/meson.build b/meson.build |
153 | int bytes, BdrvRequestFlags read_flags, | 68 | index XXXXXXX..XXXXXXX 100644 |
154 | BdrvRequestFlags write_flags); | 69 | --- a/meson.build |
155 | 70 | +++ b/meson.build | |
156 | +const BdrvChild *blk_root(BlockBackend *blk); | 71 | @@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil', |
157 | + | 72 | sources: util_ss.sources() + stub_ss.sources() + genh, |
158 | #endif | 73 | dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman]) |
159 | diff --git a/block.c b/block.c | 74 | qemuutil = declare_dependency(link_with: libqemuutil, |
160 | index XXXXXXX..XXXXXXX 100644 | 75 | - sources: genh + version_res) |
161 | --- a/block.c | 76 | + sources: genh + version_res, |
162 | +++ b/block.c | 77 | + dependencies: [event_loop_base]) |
163 | @@ -XXX,XX +XXX,XX @@ BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) | 78 | |
164 | return list; | 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 | } | ||
165 | } | 123 | } |
166 | 124 | ||
167 | +#define QAPI_LIST_ADD(list, element) do { \ | 125 | +static bool event_loop_base_can_be_deleted(UserCreatable *uc) |
168 | + typeof(list) _tmp = g_new(typeof(*(list)), 1); \ | 126 | +{ |
169 | + _tmp->value = (element); \ | 127 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc); |
170 | + _tmp->next = (list); \ | 128 | + EventLoopBase *backend = EVENT_LOOP_BASE(uc); |
171 | + (list) = _tmp; \ | 129 | + |
172 | +} while (0) | 130 | + if (bc->can_be_deleted) { |
173 | + | 131 | + return bc->can_be_deleted(backend); |
174 | +typedef struct XDbgBlockGraphConstructor { | ||
175 | + XDbgBlockGraph *graph; | ||
176 | + GHashTable *graph_nodes; | ||
177 | +} XDbgBlockGraphConstructor; | ||
178 | + | ||
179 | +static XDbgBlockGraphConstructor *xdbg_graph_new(void) | ||
180 | +{ | ||
181 | + XDbgBlockGraphConstructor *gr = g_new(XDbgBlockGraphConstructor, 1); | ||
182 | + | ||
183 | + gr->graph = g_new0(XDbgBlockGraph, 1); | ||
184 | + gr->graph_nodes = g_hash_table_new(NULL, NULL); | ||
185 | + | ||
186 | + return gr; | ||
187 | +} | ||
188 | + | ||
189 | +static XDbgBlockGraph *xdbg_graph_finalize(XDbgBlockGraphConstructor *gr) | ||
190 | +{ | ||
191 | + XDbgBlockGraph *graph = gr->graph; | ||
192 | + | ||
193 | + g_hash_table_destroy(gr->graph_nodes); | ||
194 | + g_free(gr); | ||
195 | + | ||
196 | + return graph; | ||
197 | +} | ||
198 | + | ||
199 | +static uintptr_t xdbg_graph_node_num(XDbgBlockGraphConstructor *gr, void *node) | ||
200 | +{ | ||
201 | + uintptr_t ret = (uintptr_t)g_hash_table_lookup(gr->graph_nodes, node); | ||
202 | + | ||
203 | + if (ret != 0) { | ||
204 | + return ret; | ||
205 | + } | 132 | + } |
206 | + | 133 | + |
207 | + /* | 134 | + return true; |
208 | + * Start counting from 1, not 0, because 0 interferes with not-found (NULL) | 135 | +} |
209 | + * answer of g_hash_table_lookup. | 136 | + |
210 | + */ | 137 | static void event_loop_base_class_init(ObjectClass *klass, void *class_data) |
211 | + ret = g_hash_table_size(gr->graph_nodes) + 1; | 138 | { |
212 | + g_hash_table_insert(gr->graph_nodes, node, (void *)ret); | 139 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); |
213 | + | 140 | ucc->complete = event_loop_base_complete; |
214 | + return ret; | 141 | + ucc->can_be_deleted = event_loop_base_can_be_deleted; |
215 | +} | 142 | |
216 | + | 143 | object_class_property_add(klass, "aio-max-batch", "int", |
217 | +static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, | 144 | event_loop_base_get_param, |
218 | + XDbgBlockGraphNodeType type, const char *name) | 145 | diff --git a/util/main-loop.c b/util/main-loop.c |
219 | +{ | 146 | index XXXXXXX..XXXXXXX 100644 |
220 | + XDbgBlockGraphNode *n; | 147 | --- a/util/main-loop.c |
221 | + | 148 | +++ b/util/main-loop.c |
222 | + n = g_new0(XDbgBlockGraphNode, 1); | 149 | @@ -XXX,XX +XXX,XX @@ |
223 | + | 150 | #include "qemu/error-report.h" |
224 | + n->id = xdbg_graph_node_num(gr, node); | 151 | #include "qemu/queue.h" |
225 | + n->type = type; | 152 | #include "qemu/compiler.h" |
226 | + n->name = g_strdup(name); | 153 | +#include "qom/object.h" |
227 | + | 154 | |
228 | + QAPI_LIST_ADD(gr->graph->nodes, n); | 155 | #ifndef _WIN32 |
229 | +} | 156 | #include <sys/wait.h> |
230 | + | 157 | @@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp) |
231 | +static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, | 158 | return 0; |
232 | + const BdrvChild *child) | 159 | } |
233 | +{ | 160 | |
234 | + typedef struct { | 161 | +static void main_loop_update_params(EventLoopBase *base, Error **errp) |
235 | + unsigned int flag; | 162 | +{ |
236 | + BlockPermission num; | 163 | + if (!qemu_aio_context) { |
237 | + } PermissionMap; | 164 | + error_setg(errp, "qemu aio context not ready"); |
238 | + | 165 | + return; |
239 | + static const PermissionMap permissions[] = { | ||
240 | + { BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ }, | ||
241 | + { BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE }, | ||
242 | + { BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED }, | ||
243 | + { BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE }, | ||
244 | + { BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD }, | ||
245 | + { 0, 0 } | ||
246 | + }; | ||
247 | + const PermissionMap *p; | ||
248 | + XDbgBlockGraphEdge *edge; | ||
249 | + | ||
250 | + QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1); | ||
251 | + | ||
252 | + edge = g_new0(XDbgBlockGraphEdge, 1); | ||
253 | + | ||
254 | + edge->parent = xdbg_graph_node_num(gr, parent); | ||
255 | + edge->child = xdbg_graph_node_num(gr, child->bs); | ||
256 | + edge->name = g_strdup(child->name); | ||
257 | + | ||
258 | + for (p = permissions; p->flag; p++) { | ||
259 | + if (p->flag & child->perm) { | ||
260 | + QAPI_LIST_ADD(edge->perm, p->num); | ||
261 | + } | ||
262 | + if (p->flag & child->shared_perm) { | ||
263 | + QAPI_LIST_ADD(edge->shared_perm, p->num); | ||
264 | + } | ||
265 | + } | 166 | + } |
266 | + | 167 | + |
267 | + QAPI_LIST_ADD(gr->graph->edges, edge); | 168 | + aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp); |
268 | +} | 169 | +} |
269 | + | 170 | + |
270 | + | 171 | +MainLoop *mloop; |
271 | +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) | 172 | + |
272 | +{ | 173 | +static void main_loop_init(EventLoopBase *base, Error **errp) |
273 | + BlockBackend *blk; | 174 | +{ |
274 | + BlockJob *job; | 175 | + MainLoop *m = MAIN_LOOP(base); |
275 | + BlockDriverState *bs; | 176 | + |
276 | + BdrvChild *child; | 177 | + if (mloop) { |
277 | + XDbgBlockGraphConstructor *gr = xdbg_graph_new(); | 178 | + error_setg(errp, "only one main-loop instance allowed"); |
278 | + | 179 | + return; |
279 | + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { | ||
280 | + char *allocated_name = NULL; | ||
281 | + const char *name = blk_name(blk); | ||
282 | + | ||
283 | + if (!*name) { | ||
284 | + name = allocated_name = blk_get_attached_dev_id(blk); | ||
285 | + } | ||
286 | + xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, | ||
287 | + name); | ||
288 | + g_free(allocated_name); | ||
289 | + if (blk_root(blk)) { | ||
290 | + xdbg_graph_add_edge(gr, blk, blk_root(blk)); | ||
291 | + } | ||
292 | + } | 180 | + } |
293 | + | 181 | + |
294 | + for (job = block_job_next(NULL); job; job = block_job_next(job)) { | 182 | + main_loop_update_params(base, errp); |
295 | + GSList *el; | 183 | + |
296 | + | 184 | + mloop = m; |
297 | + xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, | 185 | + return; |
298 | + job->job.id); | 186 | +} |
299 | + for (el = job->nodes; el; el = el->next) { | 187 | + |
300 | + xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); | 188 | +static bool main_loop_can_be_deleted(EventLoopBase *base) |
301 | + } | 189 | +{ |
302 | + } | 190 | + return false; |
303 | + | 191 | +} |
304 | + QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { | 192 | + |
305 | + xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, | 193 | +static void main_loop_class_init(ObjectClass *oc, void *class_data) |
306 | + bs->node_name); | 194 | +{ |
307 | + QLIST_FOREACH(child, &bs->children, next) { | 195 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); |
308 | + xdbg_graph_add_edge(gr, bs, child); | 196 | + |
309 | + } | 197 | + bc->init = main_loop_init; |
310 | + } | 198 | + bc->update_params = main_loop_update_params; |
311 | + | 199 | + bc->can_be_deleted = main_loop_can_be_deleted; |
312 | + return xdbg_graph_finalize(gr); | 200 | +} |
313 | +} | 201 | + |
314 | + | 202 | +static const TypeInfo main_loop_info = { |
315 | BlockDriverState *bdrv_lookup_bs(const char *device, | 203 | + .name = TYPE_MAIN_LOOP, |
316 | const char *node_name, | 204 | + .parent = TYPE_EVENT_LOOP_BASE, |
317 | Error **errp) | 205 | + .class_init = main_loop_class_init, |
318 | diff --git a/block/block-backend.c b/block/block-backend.c | 206 | + .instance_size = sizeof(MainLoop), |
319 | index XXXXXXX..XXXXXXX 100644 | 207 | +}; |
320 | --- a/block/block-backend.c | 208 | + |
321 | +++ b/block/block-backend.c | 209 | +static void main_loop_register_types(void) |
322 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, | 210 | +{ |
323 | blk_out->root, off_out, | 211 | + type_register_static(&main_loop_info); |
324 | bytes, read_flags, write_flags); | 212 | +} |
325 | } | 213 | + |
326 | + | 214 | +type_init(main_loop_register_types) |
327 | +const BdrvChild *blk_root(BlockBackend *blk) | 215 | + |
328 | +{ | 216 | static int max_priority; |
329 | + return blk->root; | 217 | |
330 | +} | 218 | #ifndef _WIN32 |
331 | diff --git a/blockdev.c b/blockdev.c | ||
332 | index XXXXXXX..XXXXXXX 100644 | ||
333 | --- a/blockdev.c | ||
334 | +++ b/blockdev.c | ||
335 | @@ -XXX,XX +XXX,XX @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) | ||
336 | return bdrv_named_nodes_list(errp); | ||
337 | } | ||
338 | |||
339 | +XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) | ||
340 | +{ | ||
341 | + return bdrv_get_xdbg_block_graph(errp); | ||
342 | +} | ||
343 | + | ||
344 | BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, | ||
345 | Error **errp) | ||
346 | { | ||
347 | -- | 219 | -- |
348 | 2.20.1 | 220 | 2.35.1 |
349 | |||
350 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | Render block nodes graph with help of graphviz. This new function is | ||
4 | for debugging, so there is no sense to put it into qemu.py as a method | ||
5 | of QEMUMachine. Let's instead put it separately. | ||
6 | |||
7 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Acked-by: Eduardo Habkost <ehabkost@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-id: 20181221170909.25584-3-vsementsov@virtuozzo.com | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | scripts/render_block_graph.py | 120 ++++++++++++++++++++++++++++++++++ | ||
14 | 1 file changed, 120 insertions(+) | ||
15 | create mode 100755 scripts/render_block_graph.py | ||
16 | |||
17 | diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py | ||
18 | new file mode 100755 | ||
19 | index XXXXXXX..XXXXXXX | ||
20 | --- /dev/null | ||
21 | +++ b/scripts/render_block_graph.py | ||
22 | @@ -XXX,XX +XXX,XX @@ | ||
23 | +#!/usr/bin/env python | ||
24 | +# | ||
25 | +# Render Qemu Block Graph | ||
26 | +# | ||
27 | +# Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. | ||
28 | +# | ||
29 | +# This program is free software; you can redistribute it and/or modify | ||
30 | +# it under the terms of the GNU General Public License as published by | ||
31 | +# the Free Software Foundation; either version 2 of the License, or | ||
32 | +# (at your option) any later version. | ||
33 | +# | ||
34 | +# This program is distributed in the hope that it will be useful, | ||
35 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
36 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
37 | +# GNU General Public License for more details. | ||
38 | +# | ||
39 | +# You should have received a copy of the GNU General Public License | ||
40 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
41 | +# | ||
42 | + | ||
43 | +import os | ||
44 | +import sys | ||
45 | +import subprocess | ||
46 | +import json | ||
47 | +from graphviz import Digraph | ||
48 | +from qemu import MonitorResponseError | ||
49 | + | ||
50 | + | ||
51 | +def perm(arr): | ||
52 | + s = 'w' if 'write' in arr else '_' | ||
53 | + s += 'r' if 'consistent-read' in arr else '_' | ||
54 | + s += 'u' if 'write-unchanged' in arr else '_' | ||
55 | + s += 'g' if 'graph-mod' in arr else '_' | ||
56 | + s += 's' if 'resize' in arr else '_' | ||
57 | + return s | ||
58 | + | ||
59 | + | ||
60 | +def render_block_graph(qmp, filename, format='png'): | ||
61 | + ''' | ||
62 | + Render graph in text (dot) representation into "@filename" and | ||
63 | + representation in @format into "@filename.@format" | ||
64 | + ''' | ||
65 | + | ||
66 | + bds_nodes = qmp.command('query-named-block-nodes') | ||
67 | + bds_nodes = {n['node-name']: n for n in bds_nodes} | ||
68 | + | ||
69 | + job_nodes = qmp.command('query-block-jobs') | ||
70 | + job_nodes = {n['device']: n for n in job_nodes} | ||
71 | + | ||
72 | + block_graph = qmp.command('x-debug-query-block-graph') | ||
73 | + | ||
74 | + graph = Digraph(comment='Block Nodes Graph') | ||
75 | + graph.format = format | ||
76 | + graph.node('permission symbols:\l' | ||
77 | + ' w - Write\l' | ||
78 | + ' r - consistent-Read\l' | ||
79 | + ' u - write - Unchanged\l' | ||
80 | + ' g - Graph-mod\l' | ||
81 | + ' s - reSize\l' | ||
82 | + 'edge label scheme:\l' | ||
83 | + ' <child type>\l' | ||
84 | + ' <perm>\l' | ||
85 | + ' <shared_perm>\l', shape='none') | ||
86 | + | ||
87 | + for n in block_graph['nodes']: | ||
88 | + if n['type'] == 'block-driver': | ||
89 | + info = bds_nodes[n['name']] | ||
90 | + label = n['name'] + ' [' + info['drv'] + ']' | ||
91 | + if info['drv'] == 'file': | ||
92 | + label += '\n' + os.path.basename(info['file']) | ||
93 | + shape = 'ellipse' | ||
94 | + elif n['type'] == 'block-job': | ||
95 | + info = job_nodes[n['name']] | ||
96 | + label = info['type'] + ' job (' + n['name'] + ')' | ||
97 | + shape = 'box' | ||
98 | + else: | ||
99 | + assert n['type'] == 'block-backend' | ||
100 | + label = n['name'] if n['name'] else 'unnamed blk' | ||
101 | + shape = 'box' | ||
102 | + | ||
103 | + graph.node(str(n['id']), label, shape=shape) | ||
104 | + | ||
105 | + for e in block_graph['edges']: | ||
106 | + label = '%s\l%s\l%s\l' % (e['name'], perm(e['perm']), | ||
107 | + perm(e['shared-perm'])) | ||
108 | + graph.edge(str(e['parent']), str(e['child']), label=label) | ||
109 | + | ||
110 | + graph.render(filename) | ||
111 | + | ||
112 | + | ||
113 | +class LibvirtGuest(): | ||
114 | + def __init__(self, name): | ||
115 | + self.name = name | ||
116 | + | ||
117 | + def command(self, cmd): | ||
118 | + # only supports qmp commands without parameters | ||
119 | + m = {'execute': cmd} | ||
120 | + ar = ['virsh', 'qemu-monitor-command', self.name, json.dumps(m)] | ||
121 | + | ||
122 | + reply = json.loads(subprocess.check_output(ar)) | ||
123 | + | ||
124 | + if 'error' in reply: | ||
125 | + raise MonitorResponseError(reply) | ||
126 | + | ||
127 | + return reply['return'] | ||
128 | + | ||
129 | + | ||
130 | +if __name__ == '__main__': | ||
131 | + obj = sys.argv[1] | ||
132 | + out = sys.argv[2] | ||
133 | + | ||
134 | + if os.path.exists(obj): | ||
135 | + # assume unix socket | ||
136 | + qmp = QEMUMonitorProtocol(obj) | ||
137 | + qmp.connect() | ||
138 | + else: | ||
139 | + # assume libvirt guest name | ||
140 | + qmp = LibvirtGuest(obj) | ||
141 | + | ||
142 | + render_block_graph(qmp, out) | ||
143 | -- | ||
144 | 2.20.1 | ||
145 | |||
146 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Laurent Vivier <lvivier@redhat.com> | ||
2 | 1 | ||
3 | Signed-off-by: Laurent Vivier <lvivier@redhat.com> | ||
4 | Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||
5 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
6 | Message-id: 20181213162727.17438-3-lvivier@redhat.com | ||
7 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
8 | --- | ||
9 | block/curl.c | 29 ++++++++--------------------- | ||
10 | block/trace-events | 9 +++++++++ | ||
11 | 2 files changed, 17 insertions(+), 21 deletions(-) | ||
12 | |||
13 | diff --git a/block/curl.c b/block/curl.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block/curl.c | ||
16 | +++ b/block/curl.c | ||
17 | @@ -XXX,XX +XXX,XX @@ | ||
18 | #include "crypto/secret.h" | ||
19 | #include <curl/curl.h> | ||
20 | #include "qemu/cutils.h" | ||
21 | +#include "trace.h" | ||
22 | |||
23 | -// #define DEBUG_CURL | ||
24 | // #define DEBUG_VERBOSE | ||
25 | |||
26 | -#ifdef DEBUG_CURL | ||
27 | -#define DEBUG_CURL_PRINT 1 | ||
28 | -#else | ||
29 | -#define DEBUG_CURL_PRINT 0 | ||
30 | -#endif | ||
31 | -#define DPRINTF(fmt, ...) \ | ||
32 | - do { \ | ||
33 | - if (DEBUG_CURL_PRINT) { \ | ||
34 | - fprintf(stderr, fmt, ## __VA_ARGS__); \ | ||
35 | - } \ | ||
36 | - } while (0) | ||
37 | - | ||
38 | #if LIBCURL_VERSION_NUM >= 0x071000 | ||
39 | /* The multi interface timer callback was introduced in 7.16.0 */ | ||
40 | #define NEED_CURL_TIMER_CALLBACK | ||
41 | @@ -XXX,XX +XXX,XX @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) | ||
42 | { | ||
43 | BDRVCURLState *s = opaque; | ||
44 | |||
45 | - DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); | ||
46 | + trace_curl_timer_cb(timeout_ms); | ||
47 | if (timeout_ms == -1) { | ||
48 | timer_del(&s->timer); | ||
49 | } else { | ||
50 | @@ -XXX,XX +XXX,XX @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||
51 | } | ||
52 | socket = NULL; | ||
53 | |||
54 | - DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); | ||
55 | + trace_curl_sock_cb(action, (int)fd); | ||
56 | switch (action) { | ||
57 | case CURL_POLL_IN: | ||
58 | aio_set_fd_handler(s->aio_context, fd, false, | ||
59 | @@ -XXX,XX +XXX,XX @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||
60 | size_t realsize = size * nmemb; | ||
61 | int i; | ||
62 | |||
63 | - DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||
64 | + trace_curl_read_cb(realsize); | ||
65 | |||
66 | if (!s || !s->orig_buf) { | ||
67 | goto read_end; | ||
68 | @@ -XXX,XX +XXX,XX @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||
69 | } | ||
70 | } | ||
71 | |||
72 | - DPRINTF("CURL: Opening %s\n", file); | ||
73 | + trace_curl_open(file); | ||
74 | qemu_co_queue_init(&s->free_state_waitq); | ||
75 | s->aio_context = bdrv_get_aio_context(bs); | ||
76 | s->url = g_strdup(file); | ||
77 | @@ -XXX,XX +XXX,XX @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | ||
78 | "Server does not support 'range' (byte ranges)."); | ||
79 | goto out; | ||
80 | } | ||
81 | - DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); | ||
82 | + trace_curl_open_size(s->len); | ||
83 | |||
84 | qemu_mutex_lock(&s->mutex); | ||
85 | curl_clean_state(state); | ||
86 | @@ -XXX,XX +XXX,XX @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) | ||
87 | state->acb[0] = acb; | ||
88 | |||
89 | snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); | ||
90 | - DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", | ||
91 | - acb->bytes, start, state->range); | ||
92 | + trace_curl_setup_preadv(acb->bytes, start, state->range); | ||
93 | curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||
94 | |||
95 | curl_multi_add_handle(s->multi, state->curl); | ||
96 | @@ -XXX,XX +XXX,XX @@ static void curl_close(BlockDriverState *bs) | ||
97 | { | ||
98 | BDRVCURLState *s = bs->opaque; | ||
99 | |||
100 | - DPRINTF("CURL: Close\n"); | ||
101 | + trace_curl_close(); | ||
102 | curl_detach_aio_context(bs); | ||
103 | qemu_mutex_destroy(&s->mutex); | ||
104 | |||
105 | diff --git a/block/trace-events b/block/trace-events | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/block/trace-events | ||
108 | +++ b/block/trace-events | ||
109 | @@ -XXX,XX +XXX,XX @@ ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" | ||
110 | ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu" | ||
111 | ssh_write_return(ssize_t ret) "sftp_write returned %zd" | ||
112 | ssh_seek(int64_t offset) "seeking to offset=%" PRIi64 | ||
113 | + | ||
114 | +# block/curl.c | ||
115 | +curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" | ||
116 | +curl_sock_cb(int action, int fd) "sock action %d on fd %d" | ||
117 | +curl_read_cb(size_t realsize) "just reading %zu bytes" | ||
118 | +curl_open(const char *file) "opening %s" | ||
119 | +curl_open_size(uint64_t size) "size = %" PRIu64 | ||
120 | +curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" PRIu64 " at %" PRIu64 " (%s)" | ||
121 | +curl_close(void) "close" | ||
122 | -- | ||
123 | 2.20.1 | ||
124 | |||
125 | diff view generated by jsdifflib |
1 | From: Laurent Vivier <lvivier@redhat.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Laurent Vivier <lvivier@redhat.com> | 3 | The thread pool regulates itself: when idle, it kills threads until |
4 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 4 | empty, when in demand, it creates new threads until full. This behaviour |
5 | Message-id: 20181213162727.17438-4-lvivier@redhat.com | 5 | doesn't play well with latency sensitive workloads where the price of |
6 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 6 | creating a new thread is too high. For example, when paired with qemu's |
7 | '-mlock', or using safety features like SafeStack, creating a new thread | ||
8 | has been measured take multiple milliseconds. | ||
9 | |||
10 | In order to mitigate this let's introduce a new 'EventLoopBase' | ||
11 | property to set the thread pool size. The threads will be created during | ||
12 | the pool's initialization or upon updating the property's value, remain | ||
13 | available during its lifetime regardless of demand, and destroyed upon | ||
14 | freeing it. A properly characterized workload will then be able to | ||
15 | configure the pool to avoid any latency spikes. | ||
16 | |||
17 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
18 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
19 | Acked-by: Markus Armbruster <armbru@redhat.com> | ||
20 | Message-id: 20220425075723.20019-4-nsaenzju@redhat.com | ||
21 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | --- | 22 | --- |
8 | block/file-posix.c | 25 ++++++------------------- | 23 | qapi/qom.json | 10 +++++- |
9 | block/trace-events | 7 +++++++ | 24 | include/block/aio.h | 10 ++++++ |
10 | 2 files changed, 13 insertions(+), 19 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(-) | ||
11 | 34 | ||
12 | diff --git a/block/file-posix.c b/block/file-posix.c | 35 | diff --git a/qapi/qom.json b/qapi/qom.json |
13 | index XXXXXXX..XXXXXXX 100644 | 36 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/block/file-posix.c | 37 | --- a/qapi/qom.json |
15 | +++ b/block/file-posix.c | 38 | +++ b/qapi/qom.json |
16 | @@ -XXX,XX +XXX,XX @@ | 39 | @@ -XXX,XX +XXX,XX @@ |
17 | #include <xfs/xfs.h> | 40 | # 0 means that the engine will use its default. |
41 | # (default: 0) | ||
42 | # | ||
43 | +# @thread-pool-min: minimum number of threads reserved in the thread pool | ||
44 | +# (default:0) | ||
45 | +# | ||
46 | +# @thread-pool-max: maximum number of threads the thread pool can contain | ||
47 | +# (default:64) | ||
48 | +# | ||
49 | # Since: 7.1 | ||
50 | ## | ||
51 | { 'struct': 'EventLoopBaseProperties', | ||
52 | - 'data': { '*aio-max-batch': 'int' } } | ||
53 | + 'data': { '*aio-max-batch': 'int', | ||
54 | + '*thread-pool-min': 'int', | ||
55 | + '*thread-pool-max': 'int' } } | ||
56 | |||
57 | ## | ||
58 | # @IothreadProperties: | ||
59 | diff --git a/include/block/aio.h b/include/block/aio.h | ||
60 | index XXXXXXX..XXXXXXX 100644 | ||
61 | --- a/include/block/aio.h | ||
62 | +++ b/include/block/aio.h | ||
63 | @@ -XXX,XX +XXX,XX @@ struct AioContext { | ||
64 | QSLIST_HEAD(, Coroutine) scheduled_coroutines; | ||
65 | QEMUBH *co_schedule_bh; | ||
66 | |||
67 | + int thread_pool_min; | ||
68 | + int thread_pool_max; | ||
69 | /* Thread pool for performing work and receiving completion callbacks. | ||
70 | * Has its own locking. | ||
71 | */ | ||
72 | @@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, | ||
73 | void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch, | ||
74 | Error **errp); | ||
75 | |||
76 | +/** | ||
77 | + * aio_context_set_thread_pool_params: | ||
78 | + * @ctx: the aio context | ||
79 | + * @min: min number of threads to have readily available in the thread pool | ||
80 | + * @min: max number of threads the thread pool can contain | ||
81 | + */ | ||
82 | +void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, | ||
83 | + int64_t max, Error **errp); | ||
18 | #endif | 84 | #endif |
19 | 85 | diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h | |
20 | -//#define DEBUG_BLOCK | 86 | index XXXXXXX..XXXXXXX 100644 |
21 | - | 87 | --- a/include/block/thread-pool.h |
22 | -#ifdef DEBUG_BLOCK | 88 | +++ b/include/block/thread-pool.h |
23 | -# define DEBUG_BLOCK_PRINT 1 | 89 | @@ -XXX,XX +XXX,XX @@ |
24 | -#else | 90 | |
25 | -# define DEBUG_BLOCK_PRINT 0 | 91 | #include "block/block.h" |
26 | -#endif | 92 | |
27 | -#define DPRINTF(fmt, ...) \ | 93 | +#define THREAD_POOL_MAX_THREADS_DEFAULT 64 |
28 | -do { \ | 94 | + |
29 | - if (DEBUG_BLOCK_PRINT) { \ | 95 | typedef int ThreadPoolFunc(void *opaque); |
30 | - printf(fmt, ## __VA_ARGS__); \ | 96 | |
31 | - } \ | 97 | typedef struct ThreadPool ThreadPool; |
32 | -} while (0) | 98 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool, |
33 | +#include "trace.h" | 99 | int coroutine_fn thread_pool_submit_co(ThreadPool *pool, |
34 | 100 | ThreadPoolFunc *func, void *arg); | |
35 | /* OS X does not have O_DSYNC */ | 101 | void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg); |
36 | #ifndef O_DSYNC | 102 | +void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); |
37 | @@ -XXX,XX +XXX,XX @@ static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) | 103 | |
38 | 104 | #endif | |
39 | if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) { | 105 | diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h |
40 | err = errno; | 106 | index XXXXXXX..XXXXXXX 100644 |
41 | - DPRINTF("cannot write zero range (%s)\n", strerror(errno)); | 107 | --- a/include/sysemu/event-loop-base.h |
42 | + trace_file_xfs_write_zeroes(strerror(errno)); | 108 | +++ b/include/sysemu/event-loop-base.h |
43 | return -err; | 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) | ||
252 | { | ||
253 | + ERRP_GUARD(); | ||
254 | + | ||
255 | if (!qemu_aio_context) { | ||
256 | error_setg(errp, "qemu aio context not ready"); | ||
257 | return; | ||
44 | } | 258 | } |
45 | 259 | ||
46 | @@ -XXX,XX +XXX,XX @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) | 260 | aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp); |
47 | 261 | + if (*errp) { | |
48 | if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { | 262 | + return; |
49 | err = errno; | 263 | + } |
50 | - DPRINTF("cannot punch hole (%s)\n", strerror(errno)); | 264 | + |
51 | + trace_file_xfs_discard(strerror(errno)); | 265 | + aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min, |
52 | return -err; | 266 | + base->thread_pool_max, errp); |
53 | } | 267 | } |
54 | 268 | ||
55 | @@ -XXX,XX +XXX,XX @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) | 269 | MainLoop *mloop; |
56 | 270 | diff --git a/util/thread-pool.c b/util/thread-pool.c | |
57 | /* If a match was found, leave the loop */ | 271 | index XXXXXXX..XXXXXXX 100644 |
58 | if (*mediaIterator != 0) { | 272 | --- a/util/thread-pool.c |
59 | - DPRINTF("Matching using %s\n", matching_array[index]); | 273 | +++ b/util/thread-pool.c |
60 | + trace_file_FindEjectableOpticalMedia(matching_array[index]); | 274 | @@ -XXX,XX +XXX,XX @@ struct ThreadPool { |
61 | mediaType = g_strdup(matching_array[index]); | 275 | QemuMutex lock; |
276 | QemuCond worker_stopped; | ||
277 | QemuSemaphore sem; | ||
278 | - int max_threads; | ||
279 | QEMUBH *new_thread_bh; | ||
280 | |||
281 | /* The following variables are only accessed from one AioContext. */ | ||
282 | @@ -XXX,XX +XXX,XX @@ struct ThreadPool { | ||
283 | int new_threads; /* backlog of threads we need to create */ | ||
284 | int pending_threads; /* threads created but not running yet */ | ||
285 | bool stopping; | ||
286 | + int min_threads; | ||
287 | + int max_threads; | ||
288 | }; | ||
289 | |||
290 | +static inline bool back_to_sleep(ThreadPool *pool, int ret) | ||
291 | +{ | ||
292 | + /* | ||
293 | + * The semaphore timed out, we should exit the loop except when: | ||
294 | + * - There is work to do, we raced with the signal. | ||
295 | + * - The max threads threshold just changed, we raced with the signal. | ||
296 | + * - The thread pool forces a minimum number of readily available threads. | ||
297 | + */ | ||
298 | + if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) || | ||
299 | + pool->cur_threads > pool->max_threads || | ||
300 | + pool->cur_threads <= pool->min_threads)) { | ||
301 | + return true; | ||
302 | + } | ||
303 | + | ||
304 | + return false; | ||
305 | +} | ||
306 | + | ||
307 | static void *worker_thread(void *opaque) | ||
308 | { | ||
309 | ThreadPool *pool = opaque; | ||
310 | @@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque) | ||
311 | ret = qemu_sem_timedwait(&pool->sem, 10000); | ||
312 | qemu_mutex_lock(&pool->lock); | ||
313 | pool->idle_threads--; | ||
314 | - } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list)); | ||
315 | - if (ret == -1 || pool->stopping) { | ||
316 | + } while (back_to_sleep(pool, ret)); | ||
317 | + if (ret == -1 || pool->stopping || | ||
318 | + pool->cur_threads > pool->max_threads) { | ||
62 | break; | 319 | break; |
63 | } | 320 | } |
64 | @@ -XXX,XX +XXX,XX @@ static bool setup_cdrom(char *bsd_path, Error **errp) | 321 | |
65 | if (partition_found == false) { | 322 | @@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg) |
66 | error_setg(errp, "Failed to find a working partition on disc"); | 323 | thread_pool_submit_aio(pool, func, arg, NULL, NULL); |
67 | } else { | 324 | } |
68 | - DPRINTF("Using %s as optical disc\n", test_partition); | 325 | |
69 | + trace_file_setup_cdrom(test_partition); | 326 | +void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) |
70 | pstrcpy(bsd_path, MAXPATHLEN, test_partition); | 327 | +{ |
71 | } | 328 | + qemu_mutex_lock(&pool->lock); |
72 | return partition_found; | 329 | + |
73 | @@ -XXX,XX +XXX,XX @@ static bool hdev_is_sg(BlockDriverState *bs) | 330 | + pool->min_threads = ctx->thread_pool_min; |
74 | 331 | + pool->max_threads = ctx->thread_pool_max; | |
75 | ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); | 332 | + |
76 | if (ret >= 0) { | 333 | + /* |
77 | - DPRINTF("SG device found: type=%d, version=%d\n", | 334 | + * We either have to: |
78 | - scsiid.scsi_type, sg_version); | 335 | + * - Increase the number available of threads until over the min_threads |
79 | + trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); | 336 | + * threshold. |
80 | return true; | 337 | + * - Decrease the number of available threads until under the max_threads |
81 | } | 338 | + * threshold. |
82 | 339 | + * - Do nothing. The current number of threads fall in between the min and | |
83 | diff --git a/block/trace-events b/block/trace-events | 340 | + * max thresholds. We'll let the pool manage itself. |
84 | index XXXXXXX..XXXXXXX 100644 | 341 | + */ |
85 | --- a/block/trace-events | 342 | + for (int i = pool->cur_threads; i < pool->min_threads; i++) { |
86 | +++ b/block/trace-events | 343 | + spawn_thread(pool); |
87 | @@ -XXX,XX +XXX,XX @@ curl_open(const char *file) "opening %s" | 344 | + } |
88 | curl_open_size(uint64_t size) "size = %" PRIu64 | 345 | + |
89 | curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" PRIu64 " at %" PRIu64 " (%s)" | 346 | + for (int i = pool->cur_threads; i > pool->max_threads; i--) { |
90 | curl_close(void) "close" | 347 | + qemu_sem_post(&pool->sem); |
91 | + | 348 | + } |
92 | +# block/file-posix.c | 349 | + |
93 | +file_xfs_write_zeroes(const char *error) "cannot write zero range (%s)" | 350 | + qemu_mutex_unlock(&pool->lock); |
94 | +file_xfs_discard(const char *error) "cannot punch hole (%s)" | 351 | +} |
95 | +file_FindEjectableOpticalMedia(const char *media) "Matching using %s" | 352 | + |
96 | +file_setup_cdrom(const char *partition) "Using %s as optical disc" | 353 | static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) |
97 | +file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" | 354 | { |
355 | if (!ctx) { | ||
356 | @@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) | ||
357 | qemu_mutex_init(&pool->lock); | ||
358 | qemu_cond_init(&pool->worker_stopped); | ||
359 | qemu_sem_init(&pool->sem, 0); | ||
360 | - pool->max_threads = 64; | ||
361 | pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); | ||
362 | |||
363 | QLIST_INIT(&pool->head); | ||
364 | QTAILQ_INIT(&pool->request_list); | ||
365 | + | ||
366 | + thread_pool_update_params(pool, ctx); | ||
367 | } | ||
368 | |||
369 | ThreadPool *thread_pool_new(AioContext *ctx) | ||
98 | -- | 370 | -- |
99 | 2.20.1 | 371 | 2.35.1 |
100 | |||
101 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Laurent Vivier <lvivier@redhat.com> | ||
2 | 1 | ||
3 | Signed-off-by: Laurent Vivier <lvivier@redhat.com> | ||
4 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
5 | Message-id: 20181213162727.17438-5-lvivier@redhat.com | ||
6 | [mreitz: Fixed sheepdog_snapshot_create_inode's format string to use | ||
7 | PRIx32 for uint32_ts] | ||
8 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
9 | --- | ||
10 | block/sheepdog.c | 47 +++++++++++++++++----------------------------- | ||
11 | block/trace-events | 14 ++++++++++++++ | ||
12 | 2 files changed, 31 insertions(+), 30 deletions(-) | ||
13 | |||
14 | diff --git a/block/sheepdog.c b/block/sheepdog.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/sheepdog.c | ||
17 | +++ b/block/sheepdog.c | ||
18 | @@ -XXX,XX +XXX,XX @@ | ||
19 | #include "sysemu/block-backend.h" | ||
20 | #include "qemu/bitops.h" | ||
21 | #include "qemu/cutils.h" | ||
22 | +#include "trace.h" | ||
23 | |||
24 | #define SD_PROTO_VER 0x01 | ||
25 | |||
26 | @@ -XXX,XX +XXX,XX @@ static inline size_t count_data_objs(const struct SheepdogInode *inode) | ||
27 | (1UL << inode->block_size_shift)); | ||
28 | } | ||
29 | |||
30 | -#undef DPRINTF | ||
31 | -#ifdef DEBUG_SDOG | ||
32 | -#define DEBUG_SDOG_PRINT 1 | ||
33 | -#else | ||
34 | -#define DEBUG_SDOG_PRINT 0 | ||
35 | -#endif | ||
36 | -#define DPRINTF(fmt, args...) \ | ||
37 | - do { \ | ||
38 | - if (DEBUG_SDOG_PRINT) { \ | ||
39 | - fprintf(stderr, "%s %d: " fmt, __func__, __LINE__, ##args); \ | ||
40 | - } \ | ||
41 | - } while (0) | ||
42 | - | ||
43 | typedef struct SheepdogAIOCB SheepdogAIOCB; | ||
44 | typedef struct BDRVSheepdogState BDRVSheepdogState; | ||
45 | |||
46 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void reconnect_to_sdog(void *opaque) | ||
47 | Error *local_err = NULL; | ||
48 | s->fd = get_sheep_fd(s, &local_err); | ||
49 | if (s->fd < 0) { | ||
50 | - DPRINTF("Wait for connection to be established\n"); | ||
51 | + trace_sheepdog_reconnect_to_sdog(); | ||
52 | error_report_err(local_err); | ||
53 | qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL); | ||
54 | } | ||
55 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn aio_read_response(void *opaque) | ||
56 | break; | ||
57 | case AIOCB_FLUSH_CACHE: | ||
58 | if (rsp.result == SD_RES_INVALID_PARMS) { | ||
59 | - DPRINTF("disable cache since the server doesn't support it\n"); | ||
60 | + trace_sheepdog_aio_read_response(); | ||
61 | s->cache_flags = SD_FLAG_CMD_DIRECT; | ||
62 | rsp.result = SD_RES_SUCCESS; | ||
63 | } | ||
64 | @@ -XXX,XX +XXX,XX @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, | ||
65 | s->discard_supported = true; | ||
66 | |||
67 | if (snap_id || tag[0]) { | ||
68 | - DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid); | ||
69 | + trace_sheepdog_open(vid); | ||
70 | s->is_snapshot = true; | ||
71 | } | ||
72 | |||
73 | @@ -XXX,XX +XXX,XX @@ static void sd_close(BlockDriverState *bs) | ||
74 | unsigned int wlen, rlen = 0; | ||
75 | int fd, ret; | ||
76 | |||
77 | - DPRINTF("%s\n", s->name); | ||
78 | + trace_sheepdog_close(s->name); | ||
79 | |||
80 | fd = connect_to_sdog(s, &local_err); | ||
81 | if (fd < 0) { | ||
82 | @@ -XXX,XX +XXX,XX @@ static int sd_create_branch(BDRVSheepdogState *s) | ||
83 | char *buf; | ||
84 | bool deleted; | ||
85 | |||
86 | - DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); | ||
87 | + trace_sheepdog_create_branch_snapshot(s->inode.vdi_id); | ||
88 | |||
89 | buf = g_malloc(SD_INODE_SIZE); | ||
90 | |||
91 | @@ -XXX,XX +XXX,XX @@ static int sd_create_branch(BDRVSheepdogState *s) | ||
92 | goto out; | ||
93 | } | ||
94 | |||
95 | - DPRINTF("%" PRIx32 " is created.\n", vid); | ||
96 | + trace_sheepdog_create_branch_created(vid); | ||
97 | |||
98 | fd = connect_to_sdog(s, &local_err); | ||
99 | if (fd < 0) { | ||
100 | @@ -XXX,XX +XXX,XX @@ static int sd_create_branch(BDRVSheepdogState *s) | ||
101 | |||
102 | s->is_snapshot = false; | ||
103 | ret = 0; | ||
104 | - DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id); | ||
105 | + trace_sheepdog_create_branch_new(s->inode.vdi_id); | ||
106 | |||
107 | out: | ||
108 | g_free(buf); | ||
109 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb) | ||
110 | } | ||
111 | |||
112 | if (create) { | ||
113 | - DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n", | ||
114 | - inode->vdi_id, oid, | ||
115 | - vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); | ||
116 | + trace_sheepdog_co_rw_vector_update(inode->vdi_id, oid, | ||
117 | + vid_to_data_oid(inode->data_vdi_id[idx], idx), | ||
118 | + idx); | ||
119 | oid = vid_to_data_oid(inode->vdi_id, idx); | ||
120 | - DPRINTF("new oid %" PRIx64 "\n", oid); | ||
121 | + trace_sheepdog_co_rw_vector_new(oid); | ||
122 | } | ||
123 | |||
124 | aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, | ||
125 | @@ -XXX,XX +XXX,XX @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||
126 | SheepdogInode *inode; | ||
127 | unsigned int datalen; | ||
128 | |||
129 | - DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " | ||
130 | - "is_snapshot %d\n", sn_info->name, sn_info->id_str, | ||
131 | - s->name, sn_info->vm_state_size, s->is_snapshot); | ||
132 | + trace_sheepdog_snapshot_create_info(sn_info->name, sn_info->id_str, s->name, | ||
133 | + sn_info->vm_state_size, s->is_snapshot); | ||
134 | |||
135 | if (s->is_snapshot) { | ||
136 | error_report("You can't create a snapshot of a snapshot VDI, " | ||
137 | @@ -XXX,XX +XXX,XX @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||
138 | return -EINVAL; | ||
139 | } | ||
140 | |||
141 | - DPRINTF("%s %s\n", sn_info->name, sn_info->id_str); | ||
142 | + trace_sheepdog_snapshot_create(sn_info->name, sn_info->id_str); | ||
143 | |||
144 | s->inode.vm_state_size = sn_info->vm_state_size; | ||
145 | s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; | ||
146 | @@ -XXX,XX +XXX,XX @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||
147 | } | ||
148 | |||
149 | memcpy(&s->inode, inode, datalen); | ||
150 | - DPRINTF("s->inode: name %s snap_id %x oid %x\n", | ||
151 | - s->inode.name, s->inode.snap_id, s->inode.vdi_id); | ||
152 | + trace_sheepdog_snapshot_create_inode(s->inode.name, s->inode.snap_id, | ||
153 | + s->inode.vdi_id); | ||
154 | |||
155 | cleanup: | ||
156 | g_free(inode); | ||
157 | diff --git a/block/trace-events b/block/trace-events | ||
158 | index XXXXXXX..XXXXXXX 100644 | ||
159 | --- a/block/trace-events | ||
160 | +++ b/block/trace-events | ||
161 | @@ -XXX,XX +XXX,XX @@ file_xfs_discard(const char *error) "cannot punch hole (%s)" | ||
162 | file_FindEjectableOpticalMedia(const char *media) "Matching using %s" | ||
163 | file_setup_cdrom(const char *partition) "Using %s as optical disc" | ||
164 | file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" | ||
165 | + | ||
166 | +# block/sheepdog.c | ||
167 | +sheepdog_reconnect_to_sdog(void) "Wait for connection to be established" | ||
168 | +sheepdog_aio_read_response(void) "disable cache since the server doesn't support it" | ||
169 | +sheepdog_open(uint32_t vid) "0x%" PRIx32 " snapshot inode was open" | ||
170 | +sheepdog_close(const char *name) "%s" | ||
171 | +sheepdog_create_branch_snapshot(uint32_t vdi) "0x%" PRIx32 " is snapshot" | ||
172 | +sheepdog_create_branch_created(uint32_t vdi) "0x%" PRIx32 " is created" | ||
173 | +sheepdog_create_branch_new(uint32_t vdi) "0x%" PRIx32 " was newly created" | ||
174 | +sheepdog_co_rw_vector_update(uint32_t vdi, uint64_t oid, uint64_t data, long idx) "update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld" | ||
175 | +sheepdog_co_rw_vector_new(uint64_t oid) "new oid 0x%" PRIx64 | ||
176 | +sheepdog_snapshot_create_info(const char *sn_name, const char *id, const char *name, int64_t size, int is_snapshot) "sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " "is_snapshot %d" | ||
177 | +sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s" | ||
178 | +sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32 | ||
179 | -- | ||
180 | 2.20.1 | ||
181 | |||
182 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: "Richard W.M. Jones" <rjones@redhat.com> | ||
2 | 1 | ||
3 | On FreeBSD 11.2: | ||
4 | |||
5 | $ nbdkit memory size=1M --run './qemu-io -f raw -c "aio_write 0 512" $nbd' | ||
6 | Parsing error: non-numeric argument, or extraneous/unrecognized suffix -- aio_write | ||
7 | |||
8 | After main option parsing, we reinitialize optind so we can parse each | ||
9 | command. However reinitializing optind to 0 does not work on FreeBSD. | ||
10 | What happens when you do this is optind remains 0 after the option | ||
11 | parsing loop, and the result is we try to parse argv[optind] == | ||
12 | argv[0] == "aio_write" as if it was the first parameter. | ||
13 | |||
14 | The FreeBSD manual page says: | ||
15 | |||
16 | In order to use getopt() to evaluate multiple sets of arguments, or to | ||
17 | evaluate a single set of arguments multiple times, the variable optreset | ||
18 | must be set to 1 before the second and each additional set of calls to | ||
19 | getopt(), and the variable optind must be reinitialized. | ||
20 | |||
21 | (From the rest of the man page it is clear that optind must be | ||
22 | reinitialized to 1). | ||
23 | |||
24 | The glibc man page says: | ||
25 | |||
26 | A program that scans multiple argument vectors, or rescans the same | ||
27 | vector more than once, and wants to make use of GNU extensions such as | ||
28 | '+' and '-' at the start of optstring, or changes the value of | ||
29 | POSIXLY_CORRECT between scans, must reinitialize getopt() by resetting | ||
30 | optind to 0, rather than the traditional value of 1. (Resetting to 0 | ||
31 | forces the invocation of an internal initialization routine that | ||
32 | rechecks POSIXLY_CORRECT and checks for GNU extensions in optstring.) | ||
33 | |||
34 | This commit introduces an OS-portability function called | ||
35 | qemu_reset_optind which provides a way of resetting optind that works | ||
36 | on FreeBSD and platforms that use optreset, while keeping it the same | ||
37 | as now on other platforms. | ||
38 | |||
39 | Note that the qemu codebase sets optind in many other places, but in | ||
40 | those other places it's setting a local variable and not using getopt. | ||
41 | This change is only needed in places where we are using getopt and the | ||
42 | associated global variable optind. | ||
43 | |||
44 | Signed-off-by: Richard W.M. Jones <rjones@redhat.com> | ||
45 | Message-id: 20190118101114.11759-2-rjones@redhat.com | ||
46 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
47 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
48 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
49 | --- | ||
50 | configure | 14 ++++++++++++++ | ||
51 | include/qemu/osdep.h | 16 ++++++++++++++++ | ||
52 | qemu-img.c | 2 +- | ||
53 | qemu-io-cmds.c | 2 +- | ||
54 | 4 files changed, 32 insertions(+), 2 deletions(-) | ||
55 | |||
56 | diff --git a/configure b/configure | ||
57 | index XXXXXXX..XXXXXXX 100755 | ||
58 | --- a/configure | ||
59 | +++ b/configure | ||
60 | @@ -XXX,XX +XXX,XX @@ if compile_prog "" "" ; then | ||
61 | signalfd=yes | ||
62 | fi | ||
63 | |||
64 | +# check if optreset global is declared by <getopt.h> | ||
65 | +optreset="no" | ||
66 | +cat > $TMPC << EOF | ||
67 | +#include <getopt.h> | ||
68 | +int main(void) { return optreset; } | ||
69 | +EOF | ||
70 | + | ||
71 | +if compile_prog "" "" ; then | ||
72 | + optreset=yes | ||
73 | +fi | ||
74 | + | ||
75 | # check if eventfd is supported | ||
76 | eventfd=no | ||
77 | cat > $TMPC << EOF | ||
78 | @@ -XXX,XX +XXX,XX @@ fi | ||
79 | if test "$signalfd" = "yes" ; then | ||
80 | echo "CONFIG_SIGNALFD=y" >> $config_host_mak | ||
81 | fi | ||
82 | +if test "$optreset" = "yes" ; then | ||
83 | + echo "HAVE_OPTRESET=y" >> $config_host_mak | ||
84 | +fi | ||
85 | if test "$tcg" = "yes"; then | ||
86 | echo "CONFIG_TCG=y" >> $config_host_mak | ||
87 | if test "$tcg_interpreter" = "yes" ; then | ||
88 | diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h | ||
89 | index XXXXXXX..XXXXXXX 100644 | ||
90 | --- a/include/qemu/osdep.h | ||
91 | +++ b/include/qemu/osdep.h | ||
92 | @@ -XXX,XX +XXX,XX @@ extern int daemon(int, int); | ||
93 | #include <ctype.h> | ||
94 | #include <errno.h> | ||
95 | #include <fcntl.h> | ||
96 | +#include <getopt.h> | ||
97 | #include <sys/stat.h> | ||
98 | #include <sys/time.h> | ||
99 | #include <assert.h> | ||
100 | @@ -XXX,XX +XXX,XX @@ extern int qemu_icache_linesize_log; | ||
101 | extern int qemu_dcache_linesize; | ||
102 | extern int qemu_dcache_linesize_log; | ||
103 | |||
104 | +/* | ||
105 | + * After using getopt or getopt_long, if you need to parse another set | ||
106 | + * of options, then you must reset optind. Unfortunately the way to | ||
107 | + * do this varies between implementations of getopt. | ||
108 | + */ | ||
109 | +static inline void qemu_reset_optind(void) | ||
110 | +{ | ||
111 | +#ifdef HAVE_OPTRESET | ||
112 | + optind = 1; | ||
113 | + optreset = 1; | ||
114 | +#else | ||
115 | + optind = 0; | ||
116 | +#endif | ||
117 | +} | ||
118 | + | ||
119 | #endif | ||
120 | diff --git a/qemu-img.c b/qemu-img.c | ||
121 | index XXXXXXX..XXXXXXX 100644 | ||
122 | --- a/qemu-img.c | ||
123 | +++ b/qemu-img.c | ||
124 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
125 | return 0; | ||
126 | } | ||
127 | argv += optind; | ||
128 | - optind = 0; | ||
129 | + qemu_reset_optind(); | ||
130 | |||
131 | if (!trace_init_backends()) { | ||
132 | exit(1); | ||
133 | diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c | ||
134 | index XXXXXXX..XXXXXXX 100644 | ||
135 | --- a/qemu-io-cmds.c | ||
136 | +++ b/qemu-io-cmds.c | ||
137 | @@ -XXX,XX +XXX,XX @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, | ||
138 | } | ||
139 | } | ||
140 | |||
141 | - optind = 0; | ||
142 | + qemu_reset_optind(); | ||
143 | return ct->cfunc(blk, argc, argv); | ||
144 | } | ||
145 | |||
146 | -- | ||
147 | 2.20.1 | ||
148 | |||
149 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Li Qiang <liq3ea@163.com> | ||
2 | 1 | ||
3 | Signed-off-by: Li Qiang <liq3ea@163.com> | ||
4 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
5 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
6 | Message-id: 20190120055558.32984-2-liq3ea@163.com | ||
7 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
8 | --- | ||
9 | hw/block/nvme.c | 2 +- | ||
10 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
11 | |||
12 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/hw/block/nvme.c | ||
15 | +++ b/hw/block/nvme.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static void nvme_instance_init(Object *obj) | ||
17 | } | ||
18 | |||
19 | static const TypeInfo nvme_info = { | ||
20 | - .name = "nvme", | ||
21 | + .name = TYPE_NVME, | ||
22 | .parent = TYPE_PCI_DEVICE, | ||
23 | .instance_size = sizeof(NvmeCtrl), | ||
24 | .class_init = nvme_class_init, | ||
25 | -- | ||
26 | 2.20.1 | ||
27 | |||
28 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Li Qiang <liq3ea@163.com> | ||
2 | 1 | ||
3 | When it is zero, it causes segv. | ||
4 | Using following command: | ||
5 | |||
6 | "-drive file=//home/test/test1.img,if=none,id=id0 | ||
7 | -device nvme,drive=id0,serial=test,num_queues=0" | ||
8 | causes following Backtrack: | ||
9 | |||
10 | Thread 4 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. | ||
11 | [Switching to Thread 0x7fffe9735700 (LWP 30952)] | ||
12 | 0x0000555555a7a77c in nvme_start_ctrl (n=0x5555577473f0) at hw/block/nvme.c:825 | ||
13 | 825 if (unlikely(n->cq[0])) { | ||
14 | (gdb) bt | ||
15 | 0 0x0000555555a7a77c in nvme_start_ctrl (n=0x5555577473f0) | ||
16 | at hw/block/nvme.c:825 | ||
17 | 1 0x0000555555a7af7f in nvme_write_bar (n=0x5555577473f0, offset=20, | ||
18 | data=4587521, size=4) at hw/block/nvme.c:969 | ||
19 | 2 0x0000555555a7b81a in nvme_mmio_write (opaque=0x5555577473f0, addr=20, | ||
20 | data=4587521, size=4) at hw/block/nvme.c:1163 | ||
21 | 3 0x0000555555869236 in memory_region_write_accessor (mr=0x555557747cd0, | ||
22 | addr=20, value=0x7fffe97320f8, size=4, shift=0, mask=4294967295, attrs=...) | ||
23 | at /home/test/qemu1/qemu/memory.c:502 | ||
24 | 4 0x0000555555869446 in access_with_adjusted_size (addr=20, | ||
25 | value=0x7fffe97320f8, size=4, access_size_min=2, access_size_max=8, | ||
26 | access_fn=0x55555586914d <memory_region_write_accessor>, | ||
27 | mr=0x555557747cd0, attrs=...) at /home/test/qemu1/qemu/memory.c:568 | ||
28 | 5 0x000055555586c479 in memory_region_dispatch_write (mr=0x555557747cd0, | ||
29 | addr=20, data=4587521, size=4, attrs=...) | ||
30 | at /home/test/qemu1/qemu/memory.c:1499 | ||
31 | 6 0x00005555558030af in flatview_write_continue (fv=0x7fffe0061130, | ||
32 | addr=4273930260, attrs=..., buf=0x7ffff7ff0028 "\001", len=4, addr1=20, | ||
33 | l=4, mr=0x555557747cd0) at /home/test/qemu1/qemu/exec.c:3234 | ||
34 | 7 0x00005555558031f9 in flatview_write (fv=0x7fffe0061130, addr=4273930260, | ||
35 | attrs=..., buf=0x7ffff7ff0028 "\001", len=4) | ||
36 | at /home/test/qemu1/qemu/exec.c:3273 | ||
37 | 8 0x00005555558034ff in address_space_write ( | ||
38 | ---Type <return> to continue, or q <return> to quit--- | ||
39 | as=0x555556758480 <address_space_memory>, addr=4273930260, attrs=..., | ||
40 | buf=0x7ffff7ff0028 "\001", len=4) at /home/test/qemu1/qemu/exec.c:3363 | ||
41 | 9 0x0000555555803550 in address_space_rw ( | ||
42 | as=0x555556758480 <address_space_memory>, addr=4273930260, attrs=..., | ||
43 | buf=0x7ffff7ff0028 "\001", len=4, is_write=true) | ||
44 | at /home/test/qemu1/qemu/exec.c:3374 | ||
45 | 10 0x00005555558884a1 in kvm_cpu_exec (cpu=0x555556920e40) | ||
46 | at /home/test/qemu1/qemu/accel/kvm/kvm-all.c:2031 | ||
47 | 11 0x000055555584cd9d in qemu_kvm_cpu_thread_fn (arg=0x555556920e40) | ||
48 | at /home/test/qemu1/qemu/cpus.c:1281 | ||
49 | 12 0x0000555555dbaf6d in qemu_thread_start (args=0x5555569438a0) | ||
50 | at util/qemu-thread-posix.c:502 | ||
51 | 13 0x00007ffff5dc86db in start_thread (arg=0x7fffe9735700) | ||
52 | at pthread_create.c:463 | ||
53 | 14 0x00007ffff5af188f in clone () | ||
54 | at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 | ||
55 | |||
56 | Signed-off-by: Li Qiang <liq3ea@163.com> | ||
57 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
58 | Message-id: 20190120055558.32984-3-liq3ea@163.com | ||
59 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
60 | --- | ||
61 | hw/block/nvme.c | 5 +++++ | ||
62 | 1 file changed, 5 insertions(+) | ||
63 | |||
64 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | ||
65 | index XXXXXXX..XXXXXXX 100644 | ||
66 | --- a/hw/block/nvme.c | ||
67 | +++ b/hw/block/nvme.c | ||
68 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
69 | int64_t bs_size; | ||
70 | uint8_t *pci_conf; | ||
71 | |||
72 | + if (!n->num_queues) { | ||
73 | + error_setg(errp, "num_queues can't be zero"); | ||
74 | + return; | ||
75 | + } | ||
76 | + | ||
77 | if (!n->conf.blk) { | ||
78 | error_setg(errp, "drive property not set"); | ||
79 | return; | ||
80 | -- | ||
81 | 2.20.1 | ||
82 | |||
83 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Li Qiang <liq3ea@163.com> | ||
2 | 1 | ||
3 | There is no need to make another reference. | ||
4 | |||
5 | Signed-off-by: Li Qiang <liq3ea@163.com> | ||
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
8 | Message-id: 20190120055558.32984-4-liq3ea@163.com | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | hw/block/nvme.c | 8 ++++---- | ||
12 | 1 file changed, 4 insertions(+), 4 deletions(-) | ||
13 | |||
14 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/hw/block/nvme.c | ||
17 | +++ b/hw/block/nvme.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
19 | pci_conf[PCI_INTERRUPT_PIN] = 1; | ||
20 | pci_config_set_prog_interface(pci_dev->config, 0x2); | ||
21 | pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS); | ||
22 | - pcie_endpoint_cap_init(&n->parent_obj, 0x80); | ||
23 | + pcie_endpoint_cap_init(pci_dev, 0x80); | ||
24 | |||
25 | n->num_namespaces = 1; | ||
26 | n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); | ||
27 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
28 | |||
29 | memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, | ||
30 | "nvme", n->reg_size); | ||
31 | - pci_register_bar(&n->parent_obj, 0, | ||
32 | + pci_register_bar(pci_dev, 0, | ||
33 | PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, | ||
34 | &n->iomem); | ||
35 | - msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4, NULL); | ||
36 | + msix_init_exclusive_bar(pci_dev, n->num_queues, 4, NULL); | ||
37 | |||
38 | id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); | ||
39 | id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); | ||
40 | @@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
41 | n->cmbuf = g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); | ||
42 | memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n, | ||
43 | "nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); | ||
44 | - pci_register_bar(&n->parent_obj, NVME_CMBLOC_BIR(n->bar.cmbloc), | ||
45 | + pci_register_bar(pci_dev, NVME_CMBLOC_BIR(n->bar.cmbloc), | ||
46 | PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | | ||
47 | PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem); | ||
48 | |||
49 | -- | ||
50 | 2.20.1 | ||
51 | |||
52 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | In some cases, we may want to deal with qemu-nbd errors (e.g. by | ||
2 | launching it in a different configuration until it no longer throws | ||
3 | any). In that case, we do not want its output ending up in the test | ||
4 | output. | ||
5 | 1 | ||
6 | It may still be useful for handling the error, though, so add a new | ||
7 | function that works basically like qemu_nbd(), only that it returns the | ||
8 | qemu-nbd output instead of making it end up in the log. In contrast to | ||
9 | qemu_img_pipe(), it does still return the exit code as well, though, | ||
10 | because that is even more important for error handling. | ||
11 | |||
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-id: 20181221234750.23577-2-mreitz@redhat.com | ||
14 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
15 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
16 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
17 | --- | ||
18 | tests/qemu-iotests/iotests.py | 14 ++++++++++++++ | ||
19 | 1 file changed, 14 insertions(+) | ||
20 | |||
21 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/tests/qemu-iotests/iotests.py | ||
24 | +++ b/tests/qemu-iotests/iotests.py | ||
25 | @@ -XXX,XX +XXX,XX @@ def qemu_nbd(*args): | ||
26 | '''Run qemu-nbd in daemon mode and return the parent's exit code''' | ||
27 | return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) | ||
28 | |||
29 | +def qemu_nbd_pipe(*args): | ||
30 | + '''Run qemu-nbd in daemon mode and return both the parent's exit code | ||
31 | + and its output''' | ||
32 | + subp = subprocess.Popen(qemu_nbd_args + ['--fork'] + list(args), | ||
33 | + stdout=subprocess.PIPE, | ||
34 | + stderr=subprocess.STDOUT, | ||
35 | + universal_newlines=True) | ||
36 | + exitcode = subp.wait() | ||
37 | + if exitcode < 0: | ||
38 | + sys.stderr.write('qemu-nbd received signal %i: %s\n' % | ||
39 | + (-exitcode, | ||
40 | + ' '.join(qemu_nbd_args + ['--fork'] + list(args)))) | ||
41 | + return exitcode, subp.communicate()[0] | ||
42 | + | ||
43 | def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): | ||
44 | '''Return True if two image files are identical''' | ||
45 | return qemu_img('compare', '-f', fmt1, | ||
46 | -- | ||
47 | 2.20.1 | ||
48 | |||
49 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | By default, qemu-nbd binds to 0.0.0.0. However, we then proceed to | ||
2 | connect to "localhost". Usually, this works out fine; but if this test | ||
3 | is run concurrently, some other test function may have bound a different | ||
4 | server to ::1 (on the same port -- you can bind different serves to the | ||
5 | same port, as long as one is on IPv4 and the other on IPv6). | ||
6 | 1 | ||
7 | So running qemu-nbd works, it can bind to 0.0.0.0:NBD_PORT. But | ||
8 | potentially a concurrent test has successfully taken [::1]:NBD_PORT. In | ||
9 | this case, trying to connect to "localhost" will lead us to the IPv6 | ||
10 | instance, where we do not want to end up. | ||
11 | |||
12 | Fix this by just binding to "localhost". This will make qemu-nbd error | ||
13 | out immediately and not give us cryptic errors later. | ||
14 | |||
15 | (Also, it will allow us to just try a different port as of a future | ||
16 | patch.) | ||
17 | |||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
19 | Message-id: 20181221234750.23577-3-mreitz@redhat.com | ||
20 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
21 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
22 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
23 | --- | ||
24 | tests/qemu-iotests/147 | 2 +- | ||
25 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
26 | |||
27 | diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 | ||
28 | index XXXXXXX..XXXXXXX 100755 | ||
29 | --- a/tests/qemu-iotests/147 | ||
30 | +++ b/tests/qemu-iotests/147 | ||
31 | @@ -XXX,XX +XXX,XX @@ class QemuNBD(NBDBlockdevAddBase): | ||
32 | self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) | ||
33 | |||
34 | def test_inet(self): | ||
35 | - self._server_up('-p', str(NBD_PORT)) | ||
36 | + self._server_up('-b', 'localhost', '-p', str(NBD_PORT)) | ||
37 | address = { 'type': 'inet', | ||
38 | 'data': { | ||
39 | 'host': 'localhost', | ||
40 | -- | ||
41 | 2.20.1 | ||
42 | |||
43 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | To do this, we need to allow creating the NBD server on various ports | ||
2 | instead of a single one (which may not even work if you run just one | ||
3 | instance, because something entirely else might be using that port). | ||
4 | 1 | ||
5 | So we just pick a random port in [32768, 32768 + 1024) and try to create | ||
6 | a server there. If that fails, we just retry until something sticks. | ||
7 | |||
8 | For the IPv6 test, we need a different range, though (just above that | ||
9 | one). This is because "localhost" resolves to both 127.0.0.1 and ::1. | ||
10 | This means that if you bind to it, it will bind to both, if possible, or | ||
11 | just one if the other is already in use. Therefore, if the IPv6 test | ||
12 | has already taken [::1]:some_port and we then try to take | ||
13 | localhost:some_port, that will work -- only the second server will be | ||
14 | bound to 127.0.0.1:some_port alone and not [::1]:some_port in addition. | ||
15 | So we have two different servers on the same port, one for IPv4 and one | ||
16 | for IPv6. | ||
17 | |||
18 | But when we then try to connect to the server through | ||
19 | localhost:some_port, we will always end up at the IPv6 one (as long as | ||
20 | it is up), and this may not be the one we want. | ||
21 | |||
22 | Thus, we must make sure not to create an IPv6-only NBD server on the | ||
23 | same port as a normal "dual-stack" NBD server -- which is done by using | ||
24 | distinct port ranges, as explained above. | ||
25 | |||
26 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
27 | Message-id: 20181221234750.23577-4-mreitz@redhat.com | ||
28 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
29 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
30 | --- | ||
31 | tests/qemu-iotests/147 | 98 +++++++++++++++++++++++++++++------------- | ||
32 | 1 file changed, 68 insertions(+), 30 deletions(-) | ||
33 | |||
34 | diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 | ||
35 | index XXXXXXX..XXXXXXX 100755 | ||
36 | --- a/tests/qemu-iotests/147 | ||
37 | +++ b/tests/qemu-iotests/147 | ||
38 | @@ -XXX,XX +XXX,XX @@ | ||
39 | # | ||
40 | |||
41 | import os | ||
42 | +import random | ||
43 | import socket | ||
44 | import stat | ||
45 | import time | ||
46 | import iotests | ||
47 | -from iotests import cachemode, imgfmt, qemu_img, qemu_nbd | ||
48 | +from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_pipe | ||
49 | |||
50 | -NBD_PORT = 10811 | ||
51 | +NBD_PORT_START = 32768 | ||
52 | +NBD_PORT_END = NBD_PORT_START + 1024 | ||
53 | +NBD_IPV6_PORT_START = NBD_PORT_END | ||
54 | +NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024 | ||
55 | |||
56 | test_img = os.path.join(iotests.test_dir, 'test.img') | ||
57 | unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') | ||
58 | @@ -XXX,XX +XXX,XX @@ class QemuNBD(NBDBlockdevAddBase): | ||
59 | except OSError: | ||
60 | pass | ||
61 | |||
62 | + def _try_server_up(self, *args): | ||
63 | + status, msg = qemu_nbd_pipe('-f', imgfmt, test_img, *args) | ||
64 | + if status == 0: | ||
65 | + return True | ||
66 | + if 'Address already in use' in msg: | ||
67 | + return False | ||
68 | + self.fail(msg) | ||
69 | + | ||
70 | def _server_up(self, *args): | ||
71 | - self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) | ||
72 | + self.assertTrue(self._try_server_up(*args)) | ||
73 | |||
74 | def test_inet(self): | ||
75 | - self._server_up('-b', 'localhost', '-p', str(NBD_PORT)) | ||
76 | + while True: | ||
77 | + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) | ||
78 | + if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)): | ||
79 | + break | ||
80 | + | ||
81 | address = { 'type': 'inet', | ||
82 | 'data': { | ||
83 | 'host': 'localhost', | ||
84 | - 'port': str(NBD_PORT) | ||
85 | + 'port': str(nbd_port) | ||
86 | } } | ||
87 | - self.client_test('nbd://localhost:%i' % NBD_PORT, | ||
88 | + self.client_test('nbd://localhost:%i' % nbd_port, | ||
89 | flatten_sock_addr(address)) | ||
90 | |||
91 | def test_unix(self): | ||
92 | @@ -XXX,XX +XXX,XX @@ class BuiltinNBD(NBDBlockdevAddBase): | ||
93 | except OSError: | ||
94 | pass | ||
95 | |||
96 | - def _server_up(self, address, export_name=None, export_name2=None): | ||
97 | + # Returns False on EADDRINUSE; fails an assertion on other errors. | ||
98 | + # Returns True on success. | ||
99 | + def _try_server_up(self, address, export_name=None, export_name2=None): | ||
100 | result = self.server.qmp('nbd-server-start', addr=address) | ||
101 | + if 'error' in result and \ | ||
102 | + 'Address already in use' in result['error']['desc']: | ||
103 | + return False | ||
104 | self.assert_qmp(result, 'return', {}) | ||
105 | |||
106 | if export_name is None: | ||
107 | @@ -XXX,XX +XXX,XX @@ class BuiltinNBD(NBDBlockdevAddBase): | ||
108 | name=export_name2) | ||
109 | self.assert_qmp(result, 'return', {}) | ||
110 | |||
111 | + return True | ||
112 | + | ||
113 | + def _server_up(self, address, export_name=None, export_name2=None): | ||
114 | + self.assertTrue(self._try_server_up(address, export_name, export_name2)) | ||
115 | |||
116 | def _server_down(self): | ||
117 | result = self.server.qmp('nbd-server-stop') | ||
118 | self.assert_qmp(result, 'return', {}) | ||
119 | |||
120 | def do_test_inet(self, export_name=None): | ||
121 | - address = { 'type': 'inet', | ||
122 | - 'data': { | ||
123 | - 'host': 'localhost', | ||
124 | - 'port': str(NBD_PORT) | ||
125 | - } } | ||
126 | - self._server_up(address, export_name) | ||
127 | + while True: | ||
128 | + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) | ||
129 | + address = { 'type': 'inet', | ||
130 | + 'data': { | ||
131 | + 'host': 'localhost', | ||
132 | + 'port': str(nbd_port) | ||
133 | + } } | ||
134 | + if self._try_server_up(address, export_name): | ||
135 | + break | ||
136 | + | ||
137 | export_name = export_name or 'nbd-export' | ||
138 | - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name), | ||
139 | + self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name), | ||
140 | flatten_sock_addr(address), export_name) | ||
141 | self._server_down() | ||
142 | |||
143 | @@ -XXX,XX +XXX,XX @@ class BuiltinNBD(NBDBlockdevAddBase): | ||
144 | self.do_test_inet('shadow') | ||
145 | |||
146 | def test_inet_two_exports(self): | ||
147 | - address = { 'type': 'inet', | ||
148 | - 'data': { | ||
149 | - 'host': 'localhost', | ||
150 | - 'port': str(NBD_PORT) | ||
151 | - } } | ||
152 | - self._server_up(address, 'exp1', 'exp2') | ||
153 | - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'), | ||
154 | + while True: | ||
155 | + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) | ||
156 | + address = { 'type': 'inet', | ||
157 | + 'data': { | ||
158 | + 'host': 'localhost', | ||
159 | + 'port': str(nbd_port) | ||
160 | + } } | ||
161 | + if self._try_server_up(address, 'exp1', 'exp2'): | ||
162 | + break | ||
163 | + | ||
164 | + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'), | ||
165 | flatten_sock_addr(address), 'exp1', 'node1', False) | ||
166 | - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'), | ||
167 | + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'), | ||
168 | flatten_sock_addr(address), 'exp2', 'node2', False) | ||
169 | result = self.vm.qmp('blockdev-del', node_name='node1') | ||
170 | self.assert_qmp(result, 'return', {}) | ||
171 | @@ -XXX,XX +XXX,XX @@ class BuiltinNBD(NBDBlockdevAddBase): | ||
172 | except socket.gaierror: | ||
173 | # IPv6 not available, skip | ||
174 | return | ||
175 | - address = { 'type': 'inet', | ||
176 | - 'data': { | ||
177 | - 'host': '::1', | ||
178 | - 'port': str(NBD_PORT), | ||
179 | - 'ipv4': False, | ||
180 | - 'ipv6': True | ||
181 | - } } | ||
182 | + | ||
183 | + while True: | ||
184 | + nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END) | ||
185 | + address = { 'type': 'inet', | ||
186 | + 'data': { | ||
187 | + 'host': '::1', | ||
188 | + 'port': str(nbd_port), | ||
189 | + 'ipv4': False, | ||
190 | + 'ipv6': True | ||
191 | + } } | ||
192 | + if self._try_server_up(address): | ||
193 | + break | ||
194 | + | ||
195 | filename = { 'driver': 'raw', | ||
196 | 'file': { | ||
197 | 'driver': 'nbd', | ||
198 | 'export': 'nbd-export', | ||
199 | 'server': flatten_sock_addr(address) | ||
200 | } } | ||
201 | - self._server_up(address) | ||
202 | self.client_test(filename, flatten_sock_addr(address), 'nbd-export') | ||
203 | self._server_down() | ||
204 | |||
205 | -- | ||
206 | 2.20.1 | ||
207 | |||
208 | diff view generated by jsdifflib |