]> Git Repo - qemu.git/blob - block/backup-top.c
block: refactor bdrv_check_request: add errp
[qemu.git] / block / backup-top.c
1 /*
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 <[email protected]>
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
37 typedef struct BDRVBackupTopState {
38     BlockCopyState *bcs;
39     BdrvChild *target;
40     bool active;
41     int64_t cluster_size;
42 } BDRVBackupTopState;
43
44 static 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
51 static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
52                                        uint64_t bytes, BdrvRequestFlags flags)
53 {
54     BDRVBackupTopState *s = bs->opaque;
55     uint64_t off, end;
56
57     if (flags & BDRV_REQ_WRITE_UNCHANGED) {
58         return 0;
59     }
60
61     off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
62     end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
63
64     return block_copy(s->bcs, off, end - off, true);
65 }
66
67 static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
68                                                int64_t offset, int bytes)
69 {
70     int ret = backup_top_cbw(bs, offset, bytes, 0);
71     if (ret < 0) {
72         return ret;
73     }
74
75     return bdrv_co_pdiscard(bs->backing, offset, bytes);
76 }
77
78 static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
79         int64_t offset, int bytes, BdrvRequestFlags flags)
80 {
81     int ret = backup_top_cbw(bs, offset, bytes, flags);
82     if (ret < 0) {
83         return ret;
84     }
85
86     return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
87 }
88
89 static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
90                                               uint64_t offset,
91                                               uint64_t bytes,
92                                               QEMUIOVector *qiov, int flags)
93 {
94     int ret = backup_top_cbw(bs, offset, bytes, flags);
95     if (ret < 0) {
96         return ret;
97     }
98
99     return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
100 }
101
102 static 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
111 static 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
124 static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
125                                   BdrvChildRole role,
126                                   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
145     if (!(role & BDRV_CHILD_FILTERED)) {
146         /*
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.
151          * Can't resize during a backup block job because we check the size
152          * only upfront.
153          */
154         *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
155         *nperm = BLK_PERM_WRITE;
156     } else {
157         /* Source child */
158         bdrv_default_perms(bs, c, role, reopen_queue,
159                            perm, shared, nperm, nshared);
160
161         if (perm & BLK_PERM_WRITE) {
162             *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
163         }
164         *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
165     }
166 }
167
168 BlockDriver 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
178     .bdrv_refresh_filename      = backup_top_refresh_filename,
179
180     .bdrv_child_perm            = backup_top_child_perm,
181
182     .is_filter = true,
183 };
184
185 BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
186                                          BlockDriverState *target,
187                                          const char *filter_node_name,
188                                          uint64_t cluster_size,
189                                          BackupPerf *perf,
190                                          BdrvRequestFlags write_flags,
191                                          BlockCopyState **bcs,
192                                          Error **errp)
193 {
194     Error *local_err = NULL;
195     BDRVBackupTopState *state;
196     BlockDriverState *top;
197     bool appended = false;
198
199     assert(source->total_sectors == target->total_sectors);
200
201     top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name,
202                                BDRV_O_RDWR, errp);
203     if (!top) {
204         return NULL;
205     }
206
207     state = top->opaque;
208     top->total_sectors = source->total_sectors;
209     top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
210             (BDRV_REQ_FUA & source->supported_write_flags);
211     top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
212             ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
213              source->supported_zero_flags);
214
215     bdrv_ref(target);
216     state->target = bdrv_attach_child(top, target, "target", &child_of_bds,
217                                       BDRV_CHILD_DATA, errp);
218     if (!state->target) {
219         bdrv_unref(target);
220         bdrv_unref(top);
221         return NULL;
222     }
223
224     bdrv_drained_begin(source);
225
226     bdrv_ref(top);
227     bdrv_append(top, source, &local_err);
228     if (local_err) {
229         error_prepend(&local_err, "Cannot append backup-top filter: ");
230         goto fail;
231     }
232     appended = true;
233
234     /*
235      * bdrv_append() finished successfully, now we can require permissions
236      * we want.
237      */
238     state->active = true;
239     bdrv_child_refresh_perms(top, top->backing, &local_err);
240     if (local_err) {
241         error_prepend(&local_err,
242                       "Cannot set permissions for backup-top filter: ");
243         goto fail;
244     }
245
246     state->cluster_size = cluster_size;
247     state->bcs = block_copy_state_new(top->backing, state->target,
248                                       cluster_size, perf->use_copy_range,
249                                       write_flags, &local_err);
250     if (local_err) {
251         error_prepend(&local_err, "Cannot create block-copy-state: ");
252         goto fail;
253     }
254     *bcs = state->bcs;
255
256     bdrv_drained_end(source);
257
258     return top;
259
260 fail:
261     if (appended) {
262         state->active = false;
263         bdrv_backup_top_drop(top);
264     } else {
265         bdrv_unref(top);
266     }
267
268     bdrv_drained_end(source);
269     error_propagate(errp, local_err);
270
271     return NULL;
272 }
273
274 void bdrv_backup_top_drop(BlockDriverState *bs)
275 {
276     BDRVBackupTopState *s = bs->opaque;
277
278     bdrv_drained_begin(bs);
279
280     block_copy_state_free(s->bcs);
281
282     s->active = false;
283     bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
284     bdrv_replace_node(bs, bs->backing->bs, &error_abort);
285     bdrv_set_backing_hd(bs, NULL, &error_abort);
286
287     bdrv_drained_end(bs);
288
289     bdrv_unref(bs);
290 }
This page took 0.04441 seconds and 4 git commands to generate.