]>
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" | |
27 | ||
28 | #include "sysemu/block-backend.h" | |
29 | #include "qemu/cutils.h" | |
30 | #include "qapi/error.h" | |
31 | #include "block/block_int.h" | |
32 | #include "block/qdict.h" | |
33 | #include "block/block-copy.h" | |
34 | ||
d003e0ae | 35 | #include "block/copy-before-write.h" |
7df7868b | 36 | |
d003e0ae | 37 | typedef struct BDRVCopyBeforeWriteState { |
7df7868b VSO |
38 | BlockCopyState *bcs; |
39 | BdrvChild *target; | |
397f4e9d | 40 | int64_t cluster_size; |
d003e0ae | 41 | } BDRVCopyBeforeWriteState; |
7df7868b | 42 | |
d003e0ae | 43 | static coroutine_fn int cbw_co_preadv( |
7df7868b VSO |
44 | BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
45 | QEMUIOVector *qiov, int flags) | |
46 | { | |
47 | return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); | |
48 | } | |
49 | ||
d003e0ae VSO |
50 | static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, |
51 | uint64_t offset, uint64_t bytes, BdrvRequestFlags flags) | |
7df7868b | 52 | { |
d003e0ae | 53 | BDRVCopyBeforeWriteState *s = bs->opaque; |
4bc267a7 VSO |
54 | uint64_t off, end; |
55 | ||
56 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { | |
57 | return 0; | |
58 | } | |
59 | ||
397f4e9d VSO |
60 | off = QEMU_ALIGN_DOWN(offset, s->cluster_size); |
61 | end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); | |
7df7868b | 62 | |
143a6384 | 63 | return block_copy(s->bcs, off, end - off, true); |
7df7868b VSO |
64 | } |
65 | ||
d003e0ae VSO |
66 | static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, |
67 | int64_t offset, int bytes) | |
7df7868b | 68 | { |
d003e0ae | 69 | int ret = cbw_do_copy_before_write(bs, offset, bytes, 0); |
7df7868b VSO |
70 | if (ret < 0) { |
71 | return ret; | |
72 | } | |
73 | ||
74 | return bdrv_co_pdiscard(bs->backing, offset, bytes); | |
75 | } | |
76 | ||
d003e0ae | 77 | static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, |
7df7868b VSO |
78 | int64_t offset, int bytes, BdrvRequestFlags flags) |
79 | { | |
d003e0ae | 80 | int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); |
7df7868b VSO |
81 | if (ret < 0) { |
82 | return ret; | |
83 | } | |
84 | ||
85 | return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); | |
86 | } | |
87 | ||
d003e0ae VSO |
88 | static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, |
89 | uint64_t offset, | |
90 | uint64_t bytes, | |
91 | QEMUIOVector *qiov, int flags) | |
7df7868b | 92 | { |
d003e0ae | 93 | int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); |
4bc267a7 VSO |
94 | if (ret < 0) { |
95 | return ret; | |
7df7868b VSO |
96 | } |
97 | ||
98 | return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); | |
99 | } | |
100 | ||
d003e0ae | 101 | static int coroutine_fn cbw_co_flush(BlockDriverState *bs) |
7df7868b VSO |
102 | { |
103 | if (!bs->backing) { | |
104 | return 0; | |
105 | } | |
106 | ||
107 | return bdrv_co_flush(bs->backing->bs); | |
108 | } | |
109 | ||
d003e0ae | 110 | static void cbw_refresh_filename(BlockDriverState *bs) |
7df7868b VSO |
111 | { |
112 | if (bs->backing == NULL) { | |
113 | /* | |
114 | * we can be here after failed bdrv_attach_child in | |
115 | * bdrv_set_backing_hd | |
116 | */ | |
117 | return; | |
118 | } | |
119 | pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), | |
120 | bs->backing->bs->filename); | |
121 | } | |
122 | ||
d003e0ae VSO |
123 | static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, |
124 | BdrvChildRole role, | |
125 | BlockReopenQueue *reopen_queue, | |
126 | uint64_t perm, uint64_t shared, | |
127 | uint64_t *nperm, uint64_t *nshared) | |
7df7868b | 128 | { |
25191e5f | 129 | if (!(role & BDRV_CHILD_FILTERED)) { |
7df7868b VSO |
130 | /* |
131 | * Target child | |
132 | * | |
133 | * Share write to target (child_file), to not interfere | |
134 | * with guest writes to its disk which may be in target backing chain. | |
958a04bd KW |
135 | * Can't resize during a backup block job because we check the size |
136 | * only upfront. | |
7df7868b | 137 | */ |
958a04bd | 138 | *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; |
7df7868b VSO |
139 | *nperm = BLK_PERM_WRITE; |
140 | } else { | |
141 | /* Source child */ | |
e5d8a406 | 142 | bdrv_default_perms(bs, c, role, reopen_queue, |
69dca43d | 143 | perm, shared, nperm, nshared); |
7df7868b VSO |
144 | |
145 | if (perm & BLK_PERM_WRITE) { | |
146 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; | |
147 | } | |
958a04bd | 148 | *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); |
7df7868b VSO |
149 | } |
150 | } | |
151 | ||
d003e0ae VSO |
152 | BlockDriver bdrv_cbw_filter = { |
153 | .format_name = "copy-before-write", | |
154 | .instance_size = sizeof(BDRVCopyBeforeWriteState), | |
7df7868b | 155 | |
d003e0ae VSO |
156 | .bdrv_co_preadv = cbw_co_preadv, |
157 | .bdrv_co_pwritev = cbw_co_pwritev, | |
158 | .bdrv_co_pwrite_zeroes = cbw_co_pwrite_zeroes, | |
159 | .bdrv_co_pdiscard = cbw_co_pdiscard, | |
160 | .bdrv_co_flush = cbw_co_flush, | |
7df7868b | 161 | |
d003e0ae | 162 | .bdrv_refresh_filename = cbw_refresh_filename, |
7df7868b | 163 | |
d003e0ae | 164 | .bdrv_child_perm = cbw_child_perm, |
7df7868b VSO |
165 | |
166 | .is_filter = true, | |
167 | }; | |
168 | ||
d003e0ae VSO |
169 | BlockDriverState *bdrv_cbw_append(BlockDriverState *source, |
170 | BlockDriverState *target, | |
171 | const char *filter_node_name, | |
172 | uint64_t cluster_size, | |
49577723 | 173 | bool compress, |
d003e0ae VSO |
174 | BlockCopyState **bcs, |
175 | Error **errp) | |
7df7868b | 176 | { |
934aee14 VSO |
177 | ERRP_GUARD(); |
178 | int ret; | |
d003e0ae | 179 | BDRVCopyBeforeWriteState *state; |
958a04bd | 180 | BlockDriverState *top; |
0df62f45 | 181 | bool appended = false; |
7df7868b | 182 | |
958a04bd KW |
183 | assert(source->total_sectors == target->total_sectors); |
184 | ||
d003e0ae | 185 | top = bdrv_new_open_driver(&bdrv_cbw_filter, filter_node_name, |
958a04bd | 186 | BDRV_O_RDWR, errp); |
7df7868b VSO |
187 | if (!top) { |
188 | return NULL; | |
189 | } | |
190 | ||
fb574de8 | 191 | state = top->opaque; |
4bc267a7 VSO |
192 | top->total_sectors = source->total_sectors; |
193 | top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | |
194 | (BDRV_REQ_FUA & source->supported_write_flags); | |
195 | top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | |
196 | ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & | |
197 | source->supported_zero_flags); | |
7df7868b VSO |
198 | |
199 | bdrv_ref(target); | |
58944401 HR |
200 | state->target = bdrv_attach_child(top, target, "target", &child_of_bds, |
201 | BDRV_CHILD_DATA, errp); | |
7df7868b VSO |
202 | if (!state->target) { |
203 | bdrv_unref(target); | |
204 | bdrv_unref(top); | |
205 | return NULL; | |
206 | } | |
207 | ||
208 | bdrv_drained_begin(source); | |
209 | ||
934aee14 VSO |
210 | ret = bdrv_append(top, source, errp); |
211 | if (ret < 0) { | |
d003e0ae | 212 | error_prepend(errp, "Cannot append copy-before-write filter: "); |
0df62f45 | 213 | goto fail; |
7df7868b | 214 | } |
0df62f45 | 215 | appended = true; |
7df7868b | 216 | |
397f4e9d | 217 | state->cluster_size = cluster_size; |
00e30f05 | 218 | state->bcs = block_copy_state_new(top->backing, state->target, |
2a6511df | 219 | cluster_size, false, compress, errp); |
934aee14 VSO |
220 | if (!state->bcs) { |
221 | error_prepend(errp, "Cannot create block-copy-state: "); | |
0df62f45 | 222 | goto fail; |
7df7868b VSO |
223 | } |
224 | *bcs = state->bcs; | |
225 | ||
226 | bdrv_drained_end(source); | |
227 | ||
228 | return top; | |
229 | ||
0df62f45 VSO |
230 | fail: |
231 | if (appended) { | |
d003e0ae | 232 | bdrv_cbw_drop(top); |
0df62f45 VSO |
233 | } else { |
234 | bdrv_unref(top); | |
235 | } | |
7df7868b | 236 | |
7df7868b | 237 | bdrv_drained_end(source); |
7df7868b VSO |
238 | |
239 | return NULL; | |
240 | } | |
241 | ||
d003e0ae | 242 | void bdrv_cbw_drop(BlockDriverState *bs) |
7df7868b | 243 | { |
d003e0ae | 244 | BDRVCopyBeforeWriteState *s = bs->opaque; |
7df7868b | 245 | |
b75d64b3 | 246 | bdrv_drop_filter(bs, &error_abort); |
7df7868b | 247 | |
503ca126 HR |
248 | block_copy_state_free(s->bcs); |
249 | ||
7df7868b | 250 | bdrv_unref(bs); |
7df7868b | 251 | } |