]>
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; | |
d003e0ae | 40 | } BDRVCopyBeforeWriteState; |
7df7868b | 41 | |
d003e0ae | 42 | static coroutine_fn int cbw_co_preadv( |
7df7868b VSO |
43 | BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
44 | QEMUIOVector *qiov, int flags) | |
45 | { | |
3c1e6327 | 46 | return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |
7df7868b VSO |
47 | } |
48 | ||
d003e0ae VSO |
49 | static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, |
50 | uint64_t offset, uint64_t bytes, BdrvRequestFlags flags) | |
7df7868b | 51 | { |
d003e0ae | 52 | BDRVCopyBeforeWriteState *s = bs->opaque; |
4bc267a7 | 53 | uint64_t off, end; |
b518e9e9 | 54 | int64_t cluster_size = block_copy_cluster_size(s->bcs); |
4bc267a7 VSO |
55 | |
56 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { | |
57 | return 0; | |
58 | } | |
59 | ||
b518e9e9 VSO |
60 | off = QEMU_ALIGN_DOWN(offset, cluster_size); |
61 | end = QEMU_ALIGN_UP(offset + bytes, 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 | ||
3c1e6327 | 74 | return bdrv_co_pdiscard(bs->file, offset, bytes); |
7df7868b VSO |
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 | ||
3c1e6327 | 85 | return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |
7df7868b VSO |
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 | ||
3c1e6327 | 98 | return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |
7df7868b VSO |
99 | } |
100 | ||
d003e0ae | 101 | static int coroutine_fn cbw_co_flush(BlockDriverState *bs) |
7df7868b | 102 | { |
3c1e6327 | 103 | if (!bs->file) { |
7df7868b VSO |
104 | return 0; |
105 | } | |
106 | ||
3c1e6327 | 107 | return bdrv_co_flush(bs->file->bs); |
7df7868b VSO |
108 | } |
109 | ||
d003e0ae | 110 | static void cbw_refresh_filename(BlockDriverState *bs) |
7df7868b | 111 | { |
7df7868b | 112 | pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), |
3c1e6327 | 113 | bs->file->bs->filename); |
7df7868b VSO |
114 | } |
115 | ||
d003e0ae VSO |
116 | static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, |
117 | BdrvChildRole role, | |
118 | BlockReopenQueue *reopen_queue, | |
119 | uint64_t perm, uint64_t shared, | |
120 | uint64_t *nperm, uint64_t *nshared) | |
7df7868b | 121 | { |
25191e5f | 122 | if (!(role & BDRV_CHILD_FILTERED)) { |
7df7868b VSO |
123 | /* |
124 | * Target child | |
125 | * | |
126 | * Share write to target (child_file), to not interfere | |
127 | * with guest writes to its disk which may be in target backing chain. | |
958a04bd KW |
128 | * Can't resize during a backup block job because we check the size |
129 | * only upfront. | |
7df7868b | 130 | */ |
958a04bd | 131 | *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; |
7df7868b VSO |
132 | *nperm = BLK_PERM_WRITE; |
133 | } else { | |
134 | /* Source child */ | |
e5d8a406 | 135 | bdrv_default_perms(bs, c, role, reopen_queue, |
69dca43d | 136 | perm, shared, nperm, nshared); |
7df7868b | 137 | |
3860c020 VSO |
138 | if (!QLIST_EMPTY(&bs->parents)) { |
139 | if (perm & BLK_PERM_WRITE) { | |
140 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; | |
141 | } | |
142 | *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); | |
7df7868b | 143 | } |
7df7868b VSO |
144 | } |
145 | } | |
146 | ||
d003e0ae VSO |
147 | BlockDriver bdrv_cbw_filter = { |
148 | .format_name = "copy-before-write", | |
149 | .instance_size = sizeof(BDRVCopyBeforeWriteState), | |
7df7868b | 150 | |
d003e0ae VSO |
151 | .bdrv_co_preadv = cbw_co_preadv, |
152 | .bdrv_co_pwritev = cbw_co_pwritev, | |
153 | .bdrv_co_pwrite_zeroes = cbw_co_pwrite_zeroes, | |
154 | .bdrv_co_pdiscard = cbw_co_pdiscard, | |
155 | .bdrv_co_flush = cbw_co_flush, | |
7df7868b | 156 | |
d003e0ae | 157 | .bdrv_refresh_filename = cbw_refresh_filename, |
7df7868b | 158 | |
d003e0ae | 159 | .bdrv_child_perm = cbw_child_perm, |
7df7868b VSO |
160 | |
161 | .is_filter = true, | |
162 | }; | |
163 | ||
d003e0ae VSO |
164 | BlockDriverState *bdrv_cbw_append(BlockDriverState *source, |
165 | BlockDriverState *target, | |
166 | const char *filter_node_name, | |
49577723 | 167 | bool compress, |
d003e0ae VSO |
168 | BlockCopyState **bcs, |
169 | Error **errp) | |
7df7868b | 170 | { |
934aee14 VSO |
171 | ERRP_GUARD(); |
172 | int ret; | |
d003e0ae | 173 | BDRVCopyBeforeWriteState *state; |
958a04bd | 174 | BlockDriverState *top; |
7df7868b | 175 | |
958a04bd KW |
176 | assert(source->total_sectors == target->total_sectors); |
177 | ||
d003e0ae | 178 | top = bdrv_new_open_driver(&bdrv_cbw_filter, filter_node_name, |
958a04bd | 179 | BDRV_O_RDWR, errp); |
7df7868b | 180 | if (!top) { |
3c1e6327 | 181 | error_prepend(errp, "Cannot open driver: "); |
7df7868b VSO |
182 | return NULL; |
183 | } | |
184 | ||
fb574de8 | 185 | state = top->opaque; |
4bc267a7 VSO |
186 | top->total_sectors = source->total_sectors; |
187 | top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | | |
188 | (BDRV_REQ_FUA & source->supported_write_flags); | |
189 | top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | | |
190 | ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & | |
191 | source->supported_zero_flags); | |
7df7868b VSO |
192 | |
193 | bdrv_ref(target); | |
58944401 HR |
194 | state->target = bdrv_attach_child(top, target, "target", &child_of_bds, |
195 | BDRV_CHILD_DATA, errp); | |
7df7868b | 196 | if (!state->target) { |
3c1e6327 | 197 | error_prepend(errp, "Cannot attach target child: "); |
7ddbce2d | 198 | goto fail; |
3c1e6327 VSO |
199 | } |
200 | ||
201 | bdrv_ref(source); | |
202 | top->file = bdrv_attach_child(top, source, "file", &child_of_bds, | |
203 | BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, | |
204 | errp); | |
205 | if (!top->file) { | |
206 | error_prepend(errp, "Cannot attach file child: "); | |
0df62f45 | 207 | goto fail; |
7df7868b VSO |
208 | } |
209 | ||
3c1e6327 VSO |
210 | state->bcs = block_copy_state_new(top->file, state->target, false, compress, |
211 | errp); | |
934aee14 VSO |
212 | if (!state->bcs) { |
213 | error_prepend(errp, "Cannot create block-copy-state: "); | |
0df62f45 | 214 | goto fail; |
7df7868b | 215 | } |
7df7868b | 216 | |
7ddbce2d VSO |
217 | bdrv_drained_begin(source); |
218 | ret = bdrv_replace_node(source, top, errp); | |
7df7868b | 219 | bdrv_drained_end(source); |
7ddbce2d VSO |
220 | if (ret < 0) { |
221 | error_prepend(errp, "Cannot append copy-before-write filter: "); | |
222 | goto fail; | |
223 | } | |
224 | ||
225 | *bcs = state->bcs; | |
7df7868b VSO |
226 | |
227 | return top; | |
228 | ||
0df62f45 | 229 | fail: |
7ddbce2d VSO |
230 | block_copy_state_free(state->bcs); |
231 | bdrv_unref(top); | |
7df7868b VSO |
232 | return NULL; |
233 | } | |
234 | ||
d003e0ae | 235 | void bdrv_cbw_drop(BlockDriverState *bs) |
7df7868b | 236 | { |
d003e0ae | 237 | BDRVCopyBeforeWriteState *s = bs->opaque; |
7df7868b | 238 | |
b75d64b3 | 239 | bdrv_drop_filter(bs, &error_abort); |
7df7868b | 240 | |
503ca126 HR |
241 | block_copy_state_free(s->bcs); |
242 | ||
7df7868b | 243 | bdrv_unref(bs); |
7df7868b | 244 | } |