blob: fe6883cc970965ff1acf3c17cbbde98355ce0486 [file] [log] [blame]
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +03001/*
2 * backup-top filter driver
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 *
7 * Copyright (c) 2018-2019 Virtuozzo International GmbH.
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
35#include "block/backup-top.h"
36
37typedef struct BDRVBackupTopState {
38 BlockCopyState *bcs;
39 BdrvChild *target;
40 bool active;
Vladimir Sementsov-Ogievskiy397f4e92020-03-11 13:30:04 +030041 int64_t cluster_size;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030042} BDRVBackupTopState;
43
44static coroutine_fn int backup_top_co_preadv(
45 BlockDriverState *bs, uint64_t offset, uint64_t bytes,
46 QEMUIOVector *qiov, int flags)
47{
48 return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
49}
50
51static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +030052 uint64_t bytes, BdrvRequestFlags flags)
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030053{
Vladimir Sementsov-Ogievskiy00e30f02019-10-01 16:14:09 +030054 BDRVBackupTopState *s = bs->opaque;
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +030055 uint64_t off, end;
56
57 if (flags & BDRV_REQ_WRITE_UNCHANGED) {
58 return 0;
59 }
60
Vladimir Sementsov-Ogievskiy397f4e92020-03-11 13:30:04 +030061 off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
62 end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030063
Vladimir Sementsov-Ogievskiy00e30f02019-10-01 16:14:09 +030064 return block_copy(s->bcs, off, end - off, NULL);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030065}
66
67static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
68 int64_t offset, int bytes)
69{
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +030070 int ret = backup_top_cbw(bs, offset, bytes, 0);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030071 if (ret < 0) {
72 return ret;
73 }
74
75 return bdrv_co_pdiscard(bs->backing, offset, bytes);
76}
77
78static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
79 int64_t offset, int bytes, BdrvRequestFlags flags)
80{
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +030081 int ret = backup_top_cbw(bs, offset, bytes, flags);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030082 if (ret < 0) {
83 return ret;
84 }
85
86 return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
87}
88
89static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
90 uint64_t offset,
91 uint64_t bytes,
92 QEMUIOVector *qiov, int flags)
93{
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +030094 int ret = backup_top_cbw(bs, offset, bytes, flags);
95 if (ret < 0) {
96 return ret;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +030097 }
98
99 return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
100}
101
102static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
103{
104 if (!bs->backing) {
105 return 0;
106 }
107
108 return bdrv_co_flush(bs->backing->bs);
109}
110
111static void backup_top_refresh_filename(BlockDriverState *bs)
112{
113 if (bs->backing == NULL) {
114 /*
115 * we can be here after failed bdrv_attach_child in
116 * bdrv_set_backing_hd
117 */
118 return;
119 }
120 pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
121 bs->backing->bs->filename);
122}
123
124static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
Max Reitzbf8e9252020-05-13 13:05:16 +0200125 BdrvChildRole role,
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300126 BlockReopenQueue *reopen_queue,
127 uint64_t perm, uint64_t shared,
128 uint64_t *nperm, uint64_t *nshared)
129{
130 BDRVBackupTopState *s = bs->opaque;
131
132 if (!s->active) {
133 /*
134 * The filter node may be in process of bdrv_append(), which firstly do
135 * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
136 * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
137 * let's require nothing during bdrv_append() and refresh permissions
138 * after it (see bdrv_backup_top_append()).
139 */
140 *nperm = 0;
141 *nshared = BLK_PERM_ALL;
142 return;
143 }
144
Max Reitz25191e52020-05-13 13:05:33 +0200145 if (!(role & BDRV_CHILD_FILTERED)) {
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300146 /*
147 * Target child
148 *
149 * Share write to target (child_file), to not interfere
150 * with guest writes to its disk which may be in target backing chain.
Kevin Wolf958a04b2020-04-30 16:27:54 +0200151 * Can't resize during a backup block job because we check the size
152 * only upfront.
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300153 */
Kevin Wolf958a04b2020-04-30 16:27:54 +0200154 *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300155 *nperm = BLK_PERM_WRITE;
156 } else {
157 /* Source child */
Max Reitze5d8a402020-05-13 13:05:44 +0200158 bdrv_default_perms(bs, c, role, reopen_queue,
Max Reitz69dca432020-05-13 13:05:39 +0200159 perm, shared, nperm, nshared);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300160
161 if (perm & BLK_PERM_WRITE) {
162 *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
163 }
Kevin Wolf958a04b2020-04-30 16:27:54 +0200164 *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300165 }
166}
167
168BlockDriver bdrv_backup_top_filter = {
169 .format_name = "backup-top",
170 .instance_size = sizeof(BDRVBackupTopState),
171
172 .bdrv_co_preadv = backup_top_co_preadv,
173 .bdrv_co_pwritev = backup_top_co_pwritev,
174 .bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
175 .bdrv_co_pdiscard = backup_top_co_pdiscard,
176 .bdrv_co_flush = backup_top_co_flush,
177
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300178 .bdrv_refresh_filename = backup_top_refresh_filename,
179
180 .bdrv_child_perm = backup_top_child_perm,
181
182 .is_filter = true,
183};
184
185BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
186 BlockDriverState *target,
187 const char *filter_node_name,
188 uint64_t cluster_size,
189 BdrvRequestFlags write_flags,
190 BlockCopyState **bcs,
191 Error **errp)
192{
193 Error *local_err = NULL;
194 BDRVBackupTopState *state;
Kevin Wolf958a04b2020-04-30 16:27:54 +0200195 BlockDriverState *top;
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300196 bool appended = false;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300197
Kevin Wolf958a04b2020-04-30 16:27:54 +0200198 assert(source->total_sectors == target->total_sectors);
199
200 top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name,
201 BDRV_O_RDWR, errp);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300202 if (!top) {
203 return NULL;
204 }
205
Eiichi Tsukatafb574de2019-12-23 18:06:32 +0900206 state = top->opaque;
Vladimir Sementsov-Ogievskiy4bc267a2020-02-07 19:12:31 +0300207 top->total_sectors = source->total_sectors;
208 top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
209 (BDRV_REQ_FUA & source->supported_write_flags);
210 top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
211 ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
212 source->supported_zero_flags);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300213
214 bdrv_ref(target);
Max Reitz58944402020-05-13 13:05:37 +0200215 state->target = bdrv_attach_child(top, target, "target", &child_of_bds,
216 BDRV_CHILD_DATA, errp);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300217 if (!state->target) {
218 bdrv_unref(target);
219 bdrv_unref(top);
220 return NULL;
221 }
222
223 bdrv_drained_begin(source);
224
225 bdrv_ref(top);
226 bdrv_append(top, source, &local_err);
227 if (local_err) {
228 error_prepend(&local_err, "Cannot append backup-top filter: ");
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300229 goto fail;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300230 }
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300231 appended = true;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300232
233 /*
234 * bdrv_append() finished successfully, now we can require permissions
235 * we want.
236 */
237 state->active = true;
238 bdrv_child_refresh_perms(top, top->backing, &local_err);
239 if (local_err) {
240 error_prepend(&local_err,
241 "Cannot set permissions for backup-top filter: ");
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300242 goto fail;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300243 }
244
Vladimir Sementsov-Ogievskiy397f4e92020-03-11 13:30:04 +0300245 state->cluster_size = cluster_size;
Vladimir Sementsov-Ogievskiy00e30f02019-10-01 16:14:09 +0300246 state->bcs = block_copy_state_new(top->backing, state->target,
247 cluster_size, write_flags, &local_err);
248 if (local_err) {
249 error_prepend(&local_err, "Cannot create block-copy-state: ");
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300250 goto fail;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300251 }
252 *bcs = state->bcs;
253
254 bdrv_drained_end(source);
255
256 return top;
257
Vladimir Sementsov-Ogievskiy0df62f42020-01-21 17:28:01 +0300258fail:
259 if (appended) {
260 state->active = false;
261 bdrv_backup_top_drop(top);
262 } else {
263 bdrv_unref(top);
264 }
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300265
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300266 bdrv_drained_end(source);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300267 error_propagate(errp, local_err);
268
269 return NULL;
270}
271
272void bdrv_backup_top_drop(BlockDriverState *bs)
273{
274 BDRVBackupTopState *s = bs->opaque;
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300275
276 bdrv_drained_begin(bs);
277
Max Reitz503ca122019-12-19 19:26:38 +0100278 block_copy_state_free(s->bcs);
279
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300280 s->active = false;
281 bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
Max Reitz2b088c62019-06-12 17:46:45 +0200282 bdrv_replace_node(bs, bs->backing->bs, &error_abort);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300283 bdrv_set_backing_hd(bs, NULL, &error_abort);
284
285 bdrv_drained_end(bs);
286
287 bdrv_unref(bs);
Vladimir Sementsov-Ogievskiy7df78682019-10-01 16:14:08 +0300288}