]>
Commit | Line | Data |
---|---|---|
5b15e2ec FE |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Fiona Ebner <f.ebner@proxmox.com> | |
3 | Date: Thu, 13 Oct 2022 11:33:50 +0200 | |
4 | Subject: [PATCH] PVE: add IOChannel implementation for savevm-async | |
5 | ||
6 | based on migration/channel-block.c and the implementation that was | |
7 | present in migration/savevm-async.c before QEMU 7.1. | |
8 | ||
9 | Passes along read/write requests to the given BlockBackend, while | |
10 | ensuring that a read request going beyond the end results in a | |
11 | graceful short read. | |
12 | ||
13 | Additionally, allows tracking the current position from the outside | |
14 | (intended to be used for progress tracking). | |
15 | ||
16 | Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> | |
17 | --- | |
0af826b4 | 18 | migration/channel-savevm-async.c | 182 +++++++++++++++++++++++++++++++ |
5b15e2ec FE |
19 | migration/channel-savevm-async.h | 51 +++++++++ |
20 | migration/meson.build | 1 + | |
0af826b4 | 21 | 3 files changed, 234 insertions(+) |
5b15e2ec FE |
22 | create mode 100644 migration/channel-savevm-async.c |
23 | create mode 100644 migration/channel-savevm-async.h | |
24 | ||
25 | diff --git a/migration/channel-savevm-async.c b/migration/channel-savevm-async.c | |
26 | new file mode 100644 | |
0af826b4 | 27 | index 0000000000..06d5484778 |
5b15e2ec FE |
28 | --- /dev/null |
29 | +++ b/migration/channel-savevm-async.c | |
0af826b4 | 30 | @@ -0,0 +1,182 @@ |
5b15e2ec FE |
31 | +/* |
32 | + * QIO Channel implementation to be used by savevm-async QMP calls | |
33 | + */ | |
34 | +#include "qemu/osdep.h" | |
35 | +#include "migration/channel-savevm-async.h" | |
36 | +#include "qapi/error.h" | |
37 | +#include "sysemu/block-backend.h" | |
38 | +#include "trace.h" | |
39 | + | |
40 | +QIOChannelSavevmAsync * | |
41 | +qio_channel_savevm_async_new(BlockBackend *be, size_t *bs_pos) | |
42 | +{ | |
43 | + QIOChannelSavevmAsync *ioc; | |
44 | + | |
45 | + ioc = QIO_CHANNEL_SAVEVM_ASYNC(object_new(TYPE_QIO_CHANNEL_SAVEVM_ASYNC)); | |
46 | + | |
47 | + bdrv_ref(blk_bs(be)); | |
48 | + ioc->be = be; | |
49 | + ioc->bs_pos = bs_pos; | |
50 | + | |
51 | + return ioc; | |
52 | +} | |
53 | + | |
54 | + | |
55 | +static void | |
56 | +qio_channel_savevm_async_finalize(Object *obj) | |
57 | +{ | |
58 | + QIOChannelSavevmAsync *ioc = QIO_CHANNEL_SAVEVM_ASYNC(obj); | |
59 | + | |
60 | + if (ioc->be) { | |
61 | + bdrv_unref(blk_bs(ioc->be)); | |
62 | + ioc->be = NULL; | |
63 | + } | |
64 | + ioc->bs_pos = NULL; | |
65 | +} | |
66 | + | |
67 | + | |
68 | +static ssize_t | |
69 | +qio_channel_savevm_async_readv(QIOChannel *ioc, | |
70 | + const struct iovec *iov, | |
71 | + size_t niov, | |
72 | + int **fds, | |
73 | + size_t *nfds, | |
74 | + Error **errp) | |
75 | +{ | |
76 | + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); | |
77 | + BlockBackend *be = saioc->be; | |
78 | + int64_t maxlen = blk_getlength(be); | |
79 | + QEMUIOVector qiov; | |
80 | + size_t size; | |
81 | + int ret; | |
82 | + | |
83 | + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); | |
84 | + | |
85 | + if (*saioc->bs_pos >= maxlen) { | |
86 | + error_setg(errp, "cannot read beyond maxlen"); | |
87 | + return -1; | |
88 | + } | |
89 | + | |
90 | + if (maxlen - *saioc->bs_pos < qiov.size) { | |
91 | + size = maxlen - *saioc->bs_pos; | |
92 | + } else { | |
93 | + size = qiov.size; | |
94 | + } | |
95 | + | |
96 | + // returns 0 on success | |
97 | + ret = blk_preadv(be, *saioc->bs_pos, size, &qiov, 0); | |
98 | + if (ret < 0) { | |
0af826b4 | 99 | + error_setg_errno(errp, -ret, "blk_preadv failed"); |
5b15e2ec FE |
100 | + return -1; |
101 | + } | |
102 | + | |
103 | + *saioc->bs_pos += size; | |
104 | + return size; | |
105 | +} | |
106 | + | |
107 | + | |
108 | +static ssize_t | |
109 | +qio_channel_savevm_async_writev(QIOChannel *ioc, | |
110 | + const struct iovec *iov, | |
111 | + size_t niov, | |
112 | + int *fds, | |
113 | + size_t nfds, | |
114 | + int flags, | |
115 | + Error **errp) | |
116 | +{ | |
117 | + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); | |
118 | + BlockBackend *be = saioc->be; | |
119 | + QEMUIOVector qiov; | |
120 | + int ret; | |
121 | + | |
122 | + qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov); | |
123 | + | |
124 | + if (qemu_in_coroutine()) { | |
125 | + ret = blk_co_pwritev(be, *saioc->bs_pos, qiov.size, &qiov, 0); | |
126 | + aio_wait_kick(); | |
127 | + } else { | |
128 | + ret = blk_pwritev(be, *saioc->bs_pos, qiov.size, &qiov, 0); | |
129 | + } | |
130 | + | |
131 | + if (ret < 0) { | |
0af826b4 FE |
132 | + error_setg_errno(errp, -ret, "blk(_co)_pwritev failed"); |
133 | + return -1; | |
5b15e2ec FE |
134 | + } |
135 | + | |
136 | + *saioc->bs_pos += qiov.size; | |
137 | + return qiov.size; | |
138 | +} | |
139 | + | |
140 | + | |
141 | +static int | |
142 | +qio_channel_savevm_async_set_blocking(QIOChannel *ioc, | |
143 | + bool enabled, | |
144 | + Error **errp) | |
145 | +{ | |
146 | + if (!enabled) { | |
147 | + error_setg(errp, "Non-blocking mode not supported for savevm-async"); | |
148 | + return -1; | |
149 | + } | |
150 | + return 0; | |
151 | +} | |
152 | + | |
153 | + | |
154 | +static int | |
155 | +qio_channel_savevm_async_close(QIOChannel *ioc, | |
156 | + Error **errp) | |
157 | +{ | |
158 | + QIOChannelSavevmAsync *saioc = QIO_CHANNEL_SAVEVM_ASYNC(ioc); | |
159 | + int rv = bdrv_flush(blk_bs(saioc->be)); | |
160 | + | |
161 | + if (rv < 0) { | |
162 | + error_setg_errno(errp, -rv, "Unable to flush VMState"); | |
163 | + return -1; | |
164 | + } | |
165 | + | |
166 | + bdrv_unref(blk_bs(saioc->be)); | |
167 | + saioc->be = NULL; | |
168 | + saioc->bs_pos = NULL; | |
169 | + | |
170 | + return 0; | |
171 | +} | |
172 | + | |
173 | + | |
174 | +static void | |
175 | +qio_channel_savevm_async_set_aio_fd_handler(QIOChannel *ioc, | |
176 | + AioContext *ctx, | |
177 | + IOHandler *io_read, | |
178 | + IOHandler *io_write, | |
179 | + void *opaque) | |
180 | +{ | |
181 | + // if channel-block starts doing something, check if this needs adaptation | |
182 | +} | |
183 | + | |
184 | + | |
185 | +static void | |
186 | +qio_channel_savevm_async_class_init(ObjectClass *klass, | |
187 | + void *class_data G_GNUC_UNUSED) | |
188 | +{ | |
189 | + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); | |
190 | + | |
191 | + ioc_klass->io_writev = qio_channel_savevm_async_writev; | |
192 | + ioc_klass->io_readv = qio_channel_savevm_async_readv; | |
193 | + ioc_klass->io_set_blocking = qio_channel_savevm_async_set_blocking; | |
194 | + ioc_klass->io_close = qio_channel_savevm_async_close; | |
195 | + ioc_klass->io_set_aio_fd_handler = qio_channel_savevm_async_set_aio_fd_handler; | |
196 | +} | |
197 | + | |
198 | +static const TypeInfo qio_channel_savevm_async_info = { | |
199 | + .parent = TYPE_QIO_CHANNEL, | |
200 | + .name = TYPE_QIO_CHANNEL_SAVEVM_ASYNC, | |
201 | + .instance_size = sizeof(QIOChannelSavevmAsync), | |
202 | + .instance_finalize = qio_channel_savevm_async_finalize, | |
203 | + .class_init = qio_channel_savevm_async_class_init, | |
204 | +}; | |
205 | + | |
206 | +static void | |
207 | +qio_channel_savevm_async_register_types(void) | |
208 | +{ | |
209 | + type_register_static(&qio_channel_savevm_async_info); | |
210 | +} | |
211 | + | |
212 | +type_init(qio_channel_savevm_async_register_types); | |
213 | diff --git a/migration/channel-savevm-async.h b/migration/channel-savevm-async.h | |
214 | new file mode 100644 | |
215 | index 0000000000..17ae2cb261 | |
216 | --- /dev/null | |
217 | +++ b/migration/channel-savevm-async.h | |
218 | @@ -0,0 +1,51 @@ | |
219 | +/* | |
220 | + * QEMU I/O channels driver for savevm-async.c | |
221 | + * | |
222 | + * Copyright (c) 2022 Proxmox Server Solutions | |
223 | + * | |
224 | + * Authors: | |
225 | + * Fiona Ebner (f.ebner@proxmox.com) | |
226 | + * | |
227 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
228 | + * See the COPYING file in the top-level directory. | |
229 | + */ | |
230 | + | |
231 | +#ifndef QIO_CHANNEL_SAVEVM_ASYNC_H | |
232 | +#define QIO_CHANNEL_SAVEVM_ASYNC_H | |
233 | + | |
234 | +#include "io/channel.h" | |
235 | +#include "qom/object.h" | |
236 | + | |
237 | +#define TYPE_QIO_CHANNEL_SAVEVM_ASYNC "qio-channel-savevm-async" | |
238 | +OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelSavevmAsync, QIO_CHANNEL_SAVEVM_ASYNC) | |
239 | + | |
240 | + | |
241 | +/** | |
242 | + * QIOChannelSavevmAsync: | |
243 | + * | |
244 | + * The QIOChannelBlock object provides a channel implementation that is able to | |
245 | + * perform I/O on any BlockBackend whose BlockDriverState directly contains a | |
246 | + * VMState (as opposed to indirectly, like qcow2). It allows tracking the | |
247 | + * current position from the outside. | |
248 | + */ | |
249 | +struct QIOChannelSavevmAsync { | |
250 | + QIOChannel parent; | |
251 | + BlockBackend *be; | |
252 | + size_t *bs_pos; | |
253 | +}; | |
254 | + | |
255 | + | |
256 | +/** | |
257 | + * qio_channel_savevm_async_new: | |
258 | + * @be: the block backend | |
259 | + * @bs_pos: used to keep track of the IOChannels current position | |
260 | + * | |
261 | + * Create a new IO channel object that can perform I/O on a BlockBackend object | |
262 | + * whose BlockDriverState directly contains a VMState. | |
263 | + * | |
264 | + * Returns: the new channel object | |
265 | + */ | |
266 | +QIOChannelSavevmAsync * | |
267 | +qio_channel_savevm_async_new(BlockBackend *be, size_t *bs_pos); | |
268 | + | |
269 | +#endif /* QIO_CHANNEL_SAVEVM_ASYNC_H */ | |
270 | diff --git a/migration/meson.build b/migration/meson.build | |
271 | index 690487cf1a..8cac83c06c 100644 | |
272 | --- a/migration/meson.build | |
273 | +++ b/migration/meson.build | |
274 | @@ -13,6 +13,7 @@ softmmu_ss.add(files( | |
275 | 'block-dirty-bitmap.c', | |
276 | 'channel.c', | |
277 | 'channel-block.c', | |
278 | + 'channel-savevm-async.c', | |
279 | 'colo-failover.c', | |
280 | 'colo.c', | |
281 | 'exec.c', |