]>
Commit | Line | Data |
---|---|---|
7df7868b | 1 | /* |
d003e0ae | 2 | * copy-before-write filter driver |
7df7868b VSO |
3 | * |
4 | * The driver performs Copy-Before-Write (CBW) operation: it is injected above | |
5 | * some node, and before each write it copies _old_ data to the target node. | |
6 | * | |
d003e0ae | 7 | * Copyright (c) 2018-2021 Virtuozzo International GmbH. |
7df7868b VSO |
8 | * |
9 | * Author: | |
10 | * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 | */ | |
25 | ||
26 | #include "qemu/osdep.h" | |
79ef0ceb | 27 | #include "qapi/qmp/qjson.h" |
7df7868b VSO |
28 | |
29 | #include "sysemu/block-backend.h" | |
30 | #include "qemu/cutils.h" | |
31 | #include "qapi/error.h" | |
32 | #include "block/block_int.h" | |
33 | #include "block/qdict.h" | |
34 | #include "block/block-copy.h" | |
35 | ||
d003e0ae | 36 | #include "block/copy-before-write.h" |
af5bcd77 | 37 | #include "block/reqlist.h" |
7df7868b | 38 | |
5f3a3cd7 VSO |
39 | #include "qapi/qapi-visit-block-core.h" |
40 | ||
d003e0ae | 41 | typedef struct BDRVCopyBeforeWriteState { |
7df7868b VSO |
42 | BlockCopyState *bcs; |
43 | BdrvChild *target; | |
f1bb39a8 | 44 | OnCbwError on_cbw_error; |
af5bcd77 VSO |
45 | |
46 | /* | |
47 | * @lock: protects access to @access_bitmap, @done_bitmap and | |
48 | * @frozen_read_reqs | |
49 | */ | |
50 | CoMutex lock; | |
51 | ||
52 | /* | |
53 | * @access_bitmap: represents areas allowed for reading by fleecing user. | |
54 | * Reading from non-dirty areas leads to -EACCES. | |
55 | */ | |
56 | BdrvDirtyBitmap *access_bitmap; | |
57 | ||
58 | /* | |
59 | * @done_bitmap: represents areas that was successfully copied to @target by | |
60 | * copy-before-write operations. | |
61 | */ | |
62 | BdrvDirtyBitmap *done_bitmap; | |
63 | ||
64 | /* | |
65 | * @frozen_read_reqs: current read requests for fleecing user in bs->file | |
66 | * node. These areas must not be rewritten by guest. | |
67 | */ | |
68 | BlockReqList frozen_read_reqs; | |
f1bb39a8 VSO |
69 | |
70 | /* | |
71 | * @snapshot_error is normally zero. But on first copy-before-write failure | |
72 | * when @on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT, @snapshot_error takes | |
73 | * value of this error (<0). After that all in-flight and further | |
74 | * snapshot-API requests will fail with that error. | |
75 | */ | |
76 | int snapshot_error; | |
d003e0ae | 77 | } BDRVCopyBeforeWriteState; |
7df7868b | 78 | |
d003e0ae | 79 | static coroutine_fn int cbw_co_preadv( |
f7ef38dd VSO |
80 | BlockDriverState *bs, int64_t offset, int64_t bytes, |
81 | QEMUIOVector *qiov, BdrvRequestFlags flags) | |
7df7868b | 82 | { |
3c1e6327 | 83 | return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |
7df7868b VSO |
84 | } |
85 | ||
af5bcd77 VSO |
86 | /* |
87 | * Do copy-before-write operation. | |
88 | * | |
89 | * On failure guest request must be failed too. | |
90 | * | |
91 | * On success, we also wait for all in-flight fleecing read requests in source | |
92 | * node, and it's guaranteed that after cbw_do_copy_before_write() successful | |
93 | * return there are no such requests and they will never appear. | |
94 | */ | |
d003e0ae VSO |
95 | static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, |
96 | uint64_t offset, uint64_t bytes, BdrvRequestFlags flags) | |
7df7868b | 97 | { |
d003e0ae | 98 | BDRVCopyBeforeWriteState *s = bs->opaque; |
af5bcd77 | 99 | int ret; |
4bc267a7 | 100 | uint64_t off, end; |
b518e9e9 | 101 | int64_t cluster_size = block_copy_cluster_size(s->bcs); |
4bc267a7 VSO |
102 | |
103 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { | |
104 | return 0; | |
105 | } | |
106 | ||
f1bb39a8 VSO |
107 | if (s->snapshot_error) { |
108 | return 0; | |
109 | } | |
110 | ||
b518e9e9 VSO |
111 | off = QEMU_ALIGN_DOWN(offset, cluster_size); |
112 | end = QEMU_ALIGN_UP(offset + bytes, cluster_size); | |
7df7868b | 113 | |
15df6e69 | 114 | ret = block_copy(s->bcs, off, end - off, true, 0, NULL, NULL); |
f1bb39a8 | 115 | if (ret < 0 && s->on_cbw_error == ON_CBW_ERROR_BREAK_GUEST_WRITE) { |
af5bcd77 VSO |
116 | return ret; |
117 | } | |
118 | ||
119 | WITH_QEMU_LOCK_GUARD(&s->lock) { | |
f1bb39a8 VSO |
120 | if (ret < 0) { |
121 | assert(s->on_cbw_error == ON_CBW_ERROR_BREAK_SNAPSHOT); | |
122 | if (!s->snapshot_error) { | |
123 | s->snapshot_error = ret; | |
124 | } | |
125 | } else { | |
126 | bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off); | |
127 | } | |
af5bcd77 VSO |
128 | reqlist_wait_all(&s->frozen_read_reqs, off, end - off, &s->lock); |
129 | } | |
130 | ||
131 | return 0; | |
7df7868b VSO |
132 | } |
133 | ||
d003e0ae | 134 | static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, |
0c802287 | 135 | int64_t offset, int64_t bytes) |
7df7868b | 136 | { |
d003e0ae | 137 | int ret = cbw_do_copy_before_write(bs, offset, bytes, 0); |
7df7868b VSO |
138 | if (ret < 0) { |
139 | return ret; | |
140 | } | |
141 | ||
3c1e6327 | 142 | return bdrv_co_pdiscard(bs->file, offset, bytes); |
7df7868b VSO |
143 | } |
144 | ||
d003e0ae | 145 | static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, |
f34b2bcf | 146 | int64_t offset, int64_t bytes, BdrvRequestFlags flags) |
7df7868b | 147 | { |
d003e0ae | 148 | int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); |
7df7868b VSO |
149 | if (ret < 0) { |
150 | return ret; | |
151 | } | |
152 | ||
3c1e6327 | 153 | return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |
7df7868b VSO |
154 | } |
155 | ||
d003e0ae | 156 | static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, |
e75abeda VSO |
157 | int64_t offset, |
158 | int64_t bytes, | |
159 | QEMUIOVector *qiov, | |
160 | BdrvRequestFlags flags) | |
7df7868b | 161 | { |
d003e0ae | 162 | int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); |
4bc267a7 VSO |
163 | if (ret < 0) { |
164 | return ret; | |
7df7868b VSO |
165 | } |
166 | ||
3c1e6327 | 167 | return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |
7df7868b VSO |
168 | } |
169 | ||
d003e0ae | 170 | static int coroutine_fn cbw_co_flush(BlockDriverState *bs) |
7df7868b | 171 | { |
3c1e6327 | 172 | if (!bs->file) { |
7df7868b VSO |
173 | return 0; |
174 | } | |
175 | ||
3c1e6327 | 176 | return bdrv_co_flush(bs->file->bs); |
7df7868b VSO |
177 | } |
178 | ||
af5bcd77 VSO |
179 | /* |
180 | * If @offset not accessible - return NULL. | |
181 | * | |
182 | * Otherwise, set @pnum to some bytes that accessible from @file (@file is set | |
183 | * to bs->file or to s->target). Return newly allocated BlockReq object that | |
184 | * should be than passed to cbw_snapshot_read_unlock(). | |
185 | * | |
186 | * It's guaranteed that guest writes will not interact in the region until | |
187 | * cbw_snapshot_read_unlock() called. | |
188 | */ | |
189 | static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs, | |
190 | int64_t offset, int64_t bytes, | |
191 | int64_t *pnum, BdrvChild **file) | |
192 | { | |
193 | BDRVCopyBeforeWriteState *s = bs->opaque; | |
194 | BlockReq *req = g_new(BlockReq, 1); | |
195 | bool done; | |
196 | ||
197 | QEMU_LOCK_GUARD(&s->lock); | |
198 | ||
f1bb39a8 VSO |
199 | if (s->snapshot_error) { |
200 | g_free(req); | |
201 | return NULL; | |
202 | } | |
203 | ||
af5bcd77 VSO |
204 | if (bdrv_dirty_bitmap_next_zero(s->access_bitmap, offset, bytes) != -1) { |
205 | g_free(req); | |
206 | return NULL; | |
207 | } | |
208 | ||
209 | done = bdrv_dirty_bitmap_status(s->done_bitmap, offset, bytes, pnum); | |
210 | if (done) { | |
211 | /* | |
212 | * Special invalid BlockReq, that is handled in | |
213 | * cbw_snapshot_read_unlock(). We don't need to lock something to read | |
214 | * from s->target. | |
215 | */ | |
216 | *req = (BlockReq) {.offset = -1, .bytes = -1}; | |
217 | *file = s->target; | |
218 | } else { | |
219 | reqlist_init_req(&s->frozen_read_reqs, req, offset, bytes); | |
220 | *file = bs->file; | |
221 | } | |
222 | ||
223 | return req; | |
224 | } | |
225 | ||
226 | static void cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req) | |
227 | { | |
228 | BDRVCopyBeforeWriteState *s = bs->opaque; | |
229 | ||
230 | if (req->offset == -1 && req->bytes == -1) { | |
231 | g_free(req); | |
232 | return; | |
233 | } | |
234 | ||
235 | QEMU_LOCK_GUARD(&s->lock); | |
236 | ||
237 | reqlist_remove_req(req); | |
238 | g_free(req); | |
239 | } | |
240 | ||
241 | static coroutine_fn int | |
242 | cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, | |
243 | QEMUIOVector *qiov, size_t qiov_offset) | |
244 | { | |
245 | BlockReq *req; | |
246 | BdrvChild *file; | |
247 | int ret; | |
248 | ||
249 | /* TODO: upgrade to async loop using AioTask */ | |
250 | while (bytes) { | |
251 | int64_t cur_bytes; | |
252 | ||
253 | req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &file); | |
254 | if (!req) { | |
255 | return -EACCES; | |
256 | } | |
257 | ||
258 | ret = bdrv_co_preadv_part(file, offset, cur_bytes, | |
259 | qiov, qiov_offset, 0); | |
260 | cbw_snapshot_read_unlock(bs, req); | |
261 | if (ret < 0) { | |
262 | return ret; | |
263 | } | |
264 | ||
265 | bytes -= cur_bytes; | |
266 | offset += cur_bytes; | |
267 | qiov_offset += cur_bytes; | |
268 | } | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static int coroutine_fn | |
274 | cbw_co_snapshot_block_status(BlockDriverState *bs, | |
275 | bool want_zero, int64_t offset, int64_t bytes, | |
276 | int64_t *pnum, int64_t *map, | |
277 | BlockDriverState **file) | |
278 | { | |
279 | BDRVCopyBeforeWriteState *s = bs->opaque; | |
280 | BlockReq *req; | |
281 | int ret; | |
282 | int64_t cur_bytes; | |
283 | BdrvChild *child; | |
284 | ||
285 | req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &child); | |
286 | if (!req) { | |
287 | return -EACCES; | |
288 | } | |
289 | ||
290 | ret = bdrv_block_status(child->bs, offset, cur_bytes, pnum, map, file); | |
291 | if (child == s->target) { | |
292 | /* | |
293 | * We refer to s->target only for areas that we've written to it. | |
294 | * And we can not report unallocated blocks in s->target: this will | |
295 | * break generic block-status-above logic, that will go to | |
296 | * copy-before-write filtered child in this case. | |
297 | */ | |
298 | assert(ret & BDRV_BLOCK_ALLOCATED); | |
299 | } | |
300 | ||
301 | cbw_snapshot_read_unlock(bs, req); | |
302 | ||
303 | return ret; | |
304 | } | |
305 | ||
306 | static int coroutine_fn cbw_co_pdiscard_snapshot(BlockDriverState *bs, | |
307 | int64_t offset, int64_t bytes) | |
308 | { | |
309 | BDRVCopyBeforeWriteState *s = bs->opaque; | |
310 | ||
311 | WITH_QEMU_LOCK_GUARD(&s->lock) { | |
312 | bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes); | |
313 | } | |
314 | ||
315 | block_copy_reset(s->bcs, offset, bytes); | |
316 | ||
317 | return bdrv_co_pdiscard(s->target, offset, bytes); | |
318 | } | |
319 | ||
d003e0ae | 320 | static void cbw_refresh_filename(BlockDriverState *bs) |
7df7868b | 321 | { |
7df7868b | 322 | pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), |
3c1e6327 | 323 | bs->file->bs->filename); |
7df7868b VSO |
324 | } |
325 | ||
d003e0ae VSO |
326 | static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, |
327 | BdrvChildRole role, | |
328 | BlockReopenQueue *reopen_queue, | |
329 | uint64_t perm, uint64_t shared, | |
330 | uint64_t *nperm, uint64_t *nshared) | |
7df7868b | 331 | { |
25191e5f | 332 | if (!(role & BDRV_CHILD_FILTERED)) { |
7df7868b VSO |
333 | /* |
334 | * Target child | |
335 | * | |
336 | * Share write to target (child_file), to not interfere | |
337 | * with guest writes to its disk which may be in target backing chain. | |
958a04bd KW |
338 | * Can't resize during a backup block job because we check the size |
339 | * only upfront. | |
7df7868b | 340 | */ |
958a04bd | 341 | *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; |
7df7868b VSO |
342 | *nperm = BLK_PERM_WRITE; |
343 | } else { | |
344 | /* Source child */ | |
e5d8a406 | 345 | bdrv_default_perms(bs, c, role, reopen_queue, |
69dca43d | 346 | perm, shared, nperm, nshared); |
7df7868b | 347 | |
3860c020 VSO |
348 | if (!QLIST_EMPTY(&bs->parents)) { |
349 | if (perm & BLK_PERM_WRITE) { | |
350 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; | |
351 | } | |
352 | *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); | |
7df7868b | 353 | } |
7df7868b VSO |
354 | } |
355 | } | |
356 | ||
79ef0ceb | 357 | static BlockdevOptions *cbw_parse_options(QDict *options, Error **errp) |
5f3a3cd7 | 358 | { |
79ef0ceb | 359 | BlockdevOptions *opts = NULL; |
5f3a3cd7 | 360 | Visitor *v = NULL; |
5f3a3cd7 | 361 | |
79ef0ceb | 362 | qdict_put_str(options, "driver", "copy-before-write"); |
5f3a3cd7 | 363 | |
79ef0ceb | 364 | v = qobject_input_visitor_new_flat_confused(options, errp); |
5f3a3cd7 VSO |
365 | if (!v) { |
366 | goto out; | |
367 | } | |
368 | ||
79ef0ceb VSO |
369 | visit_type_BlockdevOptions(v, NULL, &opts, errp); |
370 | if (!opts) { | |
5f3a3cd7 VSO |
371 | goto out; |
372 | } | |
373 | ||
79ef0ceb VSO |
374 | /* |
375 | * Delete options which we are going to parse through BlockdevOptions | |
376 | * object for original options. | |
377 | */ | |
378 | qdict_extract_subqdict(options, NULL, "bitmap"); | |
f1bb39a8 | 379 | qdict_del(options, "on-cbw-error"); |
5f3a3cd7 VSO |
380 | |
381 | out: | |
5f3a3cd7 | 382 | visit_free(v); |
79ef0ceb | 383 | qdict_del(options, "driver"); |
5f3a3cd7 | 384 | |
79ef0ceb | 385 | return opts; |
5f3a3cd7 VSO |
386 | } |
387 | ||
751cec7a VSO |
388 | static int cbw_open(BlockDriverState *bs, QDict *options, int flags, |
389 | Error **errp) | |
1f0cacb9 | 390 | { |
fe7ea40c | 391 | BDRVCopyBeforeWriteState *s = bs->opaque; |
5f3a3cd7 | 392 | BdrvDirtyBitmap *bitmap = NULL; |
af5bcd77 | 393 | int64_t cluster_size; |
79ef0ceb VSO |
394 | g_autoptr(BlockdevOptions) full_opts = NULL; |
395 | BlockdevOptionsCbw *opts; | |
396 | ||
397 | full_opts = cbw_parse_options(options, errp); | |
398 | if (!full_opts) { | |
399 | return -EINVAL; | |
400 | } | |
401 | assert(full_opts->driver == BLOCKDEV_DRIVER_COPY_BEFORE_WRITE); | |
402 | opts = &full_opts->u.copy_before_write; | |
1f0cacb9 | 403 | |
f44fd739 VSO |
404 | bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, |
405 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | |
406 | false, errp); | |
407 | if (!bs->file) { | |
1f0cacb9 VSO |
408 | return -EINVAL; |
409 | } | |
410 | ||
f44fd739 VSO |
411 | s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds, |
412 | BDRV_CHILD_DATA, false, errp); | |
413 | if (!s->target) { | |
1f0cacb9 VSO |
414 | return -EINVAL; |
415 | } | |
416 | ||
79ef0ceb VSO |
417 | if (opts->has_bitmap) { |
418 | bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, | |
419 | opts->bitmap->name, NULL, errp); | |
420 | if (!bitmap) { | |
421 | return -EINVAL; | |
422 | } | |
5f3a3cd7 | 423 | } |
f1bb39a8 VSO |
424 | s->on_cbw_error = opts->has_on_cbw_error ? opts->on_cbw_error : |
425 | ON_CBW_ERROR_BREAK_GUEST_WRITE; | |
5f3a3cd7 | 426 | |
5a507426 VSO |
427 | bs->total_sectors = bs->file->bs->total_sectors; |
428 | bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | |
429 | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); | |
430 | bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | |
431 | ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & | |
432 | bs->file->bs->supported_zero_flags); | |
433 | ||
5f3a3cd7 | 434 | s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp); |
fe7ea40c | 435 | if (!s->bcs) { |
1f0cacb9 VSO |
436 | error_prepend(errp, "Cannot create block-copy-state: "); |
437 | return -EINVAL; | |
438 | } | |
439 | ||
af5bcd77 VSO |
440 | cluster_size = block_copy_cluster_size(s->bcs); |
441 | ||
442 | s->done_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); | |
443 | if (!s->done_bitmap) { | |
444 | return -EINVAL; | |
445 | } | |
446 | bdrv_disable_dirty_bitmap(s->done_bitmap); | |
447 | ||
448 | /* s->access_bitmap starts equal to bcs bitmap */ | |
449 | s->access_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp); | |
450 | if (!s->access_bitmap) { | |
451 | return -EINVAL; | |
452 | } | |
453 | bdrv_disable_dirty_bitmap(s->access_bitmap); | |
454 | bdrv_dirty_bitmap_merge_internal(s->access_bitmap, | |
455 | block_copy_dirty_bitmap(s->bcs), NULL, | |
456 | true); | |
457 | ||
458 | qemu_co_mutex_init(&s->lock); | |
459 | QLIST_INIT(&s->frozen_read_reqs); | |
460 | ||
1f0cacb9 VSO |
461 | return 0; |
462 | } | |
463 | ||
751cec7a VSO |
464 | static void cbw_close(BlockDriverState *bs) |
465 | { | |
466 | BDRVCopyBeforeWriteState *s = bs->opaque; | |
467 | ||
af5bcd77 VSO |
468 | bdrv_release_dirty_bitmap(s->access_bitmap); |
469 | bdrv_release_dirty_bitmap(s->done_bitmap); | |
470 | ||
751cec7a VSO |
471 | block_copy_state_free(s->bcs); |
472 | s->bcs = NULL; | |
473 | } | |
474 | ||
d003e0ae VSO |
475 | BlockDriver bdrv_cbw_filter = { |
476 | .format_name = "copy-before-write", | |
477 | .instance_size = sizeof(BDRVCopyBeforeWriteState), | |
7df7868b | 478 | |
751cec7a VSO |
479 | .bdrv_open = cbw_open, |
480 | .bdrv_close = cbw_close, | |
481 | ||
d003e0ae VSO |
482 | .bdrv_co_preadv = cbw_co_preadv, |
483 | .bdrv_co_pwritev = cbw_co_pwritev, | |
484 | .bdrv_co_pwrite_zeroes = cbw_co_pwrite_zeroes, | |
485 | .bdrv_co_pdiscard = cbw_co_pdiscard, | |
486 | .bdrv_co_flush = cbw_co_flush, | |
7df7868b | 487 | |
af5bcd77 VSO |
488 | .bdrv_co_preadv_snapshot = cbw_co_preadv_snapshot, |
489 | .bdrv_co_pdiscard_snapshot = cbw_co_pdiscard_snapshot, | |
490 | .bdrv_co_snapshot_block_status = cbw_co_snapshot_block_status, | |
491 | ||
d003e0ae | 492 | .bdrv_refresh_filename = cbw_refresh_filename, |
7df7868b | 493 | |
d003e0ae | 494 | .bdrv_child_perm = cbw_child_perm, |
7df7868b VSO |
495 | |
496 | .is_filter = true, | |
497 | }; | |
498 | ||
d003e0ae VSO |
499 | BlockDriverState *bdrv_cbw_append(BlockDriverState *source, |
500 | BlockDriverState *target, | |
501 | const char *filter_node_name, | |
d003e0ae VSO |
502 | BlockCopyState **bcs, |
503 | Error **errp) | |
7df7868b | 504 | { |
934aee14 | 505 | ERRP_GUARD(); |
d003e0ae | 506 | BDRVCopyBeforeWriteState *state; |
958a04bd | 507 | BlockDriverState *top; |
f44fd739 | 508 | QDict *opts; |
7df7868b | 509 | |
958a04bd | 510 | assert(source->total_sectors == target->total_sectors); |
377cc15b | 511 | GLOBAL_STATE_CODE(); |
958a04bd | 512 | |
f44fd739 | 513 | opts = qdict_new(); |
751cec7a VSO |
514 | qdict_put_str(opts, "driver", "copy-before-write"); |
515 | if (filter_node_name) { | |
516 | qdict_put_str(opts, "node-name", filter_node_name); | |
517 | } | |
f44fd739 VSO |
518 | qdict_put_str(opts, "file", bdrv_get_node_name(source)); |
519 | qdict_put_str(opts, "target", bdrv_get_node_name(target)); | |
520 | ||
751cec7a VSO |
521 | top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp); |
522 | if (!top) { | |
523 | return NULL; | |
7ddbce2d VSO |
524 | } |
525 | ||
751cec7a | 526 | state = top->opaque; |
7ddbce2d | 527 | *bcs = state->bcs; |
7df7868b VSO |
528 | |
529 | return top; | |
7df7868b VSO |
530 | } |
531 | ||
d003e0ae | 532 | void bdrv_cbw_drop(BlockDriverState *bs) |
7df7868b | 533 | { |
377cc15b | 534 | GLOBAL_STATE_CODE(); |
b75d64b3 | 535 | bdrv_drop_filter(bs, &error_abort); |
7df7868b | 536 | bdrv_unref(bs); |
7df7868b | 537 | } |
751cec7a VSO |
538 | |
539 | static void cbw_init(void) | |
540 | { | |
541 | bdrv_register(&bdrv_cbw_filter); | |
542 | } | |
543 | ||
544 | block_init(cbw_init); |