]> Git Repo - qemu.git/blob - block/backup-top.c
audio: make mixeng optional
[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 } BDRVBackupTopState;
42
43 static coroutine_fn int backup_top_co_preadv(
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
50 static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
51                                        uint64_t bytes)
52 {
53     BDRVBackupTopState *s = bs->opaque;
54     uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size);
55     uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size);
56
57     return block_copy(s->bcs, off, end - off, NULL);
58 }
59
60 static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
61                                                int64_t offset, int bytes)
62 {
63     int ret = backup_top_cbw(bs, offset, bytes);
64     if (ret < 0) {
65         return ret;
66     }
67
68     return bdrv_co_pdiscard(bs->backing, offset, bytes);
69 }
70
71 static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
72         int64_t offset, int bytes, BdrvRequestFlags flags)
73 {
74     int ret = backup_top_cbw(bs, offset, bytes);
75     if (ret < 0) {
76         return ret;
77     }
78
79     return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
80 }
81
82 static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
83                                               uint64_t offset,
84                                               uint64_t bytes,
85                                               QEMUIOVector *qiov, int flags)
86 {
87     if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) {
88         int ret = backup_top_cbw(bs, offset, bytes);
89         if (ret < 0) {
90             return ret;
91         }
92     }
93
94     return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
95 }
96
97 static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
98 {
99     if (!bs->backing) {
100         return 0;
101     }
102
103     return bdrv_co_flush(bs->backing->bs);
104 }
105
106 static void backup_top_refresh_filename(BlockDriverState *bs)
107 {
108     if (bs->backing == NULL) {
109         /*
110          * we can be here after failed bdrv_attach_child in
111          * bdrv_set_backing_hd
112          */
113         return;
114     }
115     pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
116             bs->backing->bs->filename);
117 }
118
119 static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
120                                   const BdrvChildRole *role,
121                                   BlockReopenQueue *reopen_queue,
122                                   uint64_t perm, uint64_t shared,
123                                   uint64_t *nperm, uint64_t *nshared)
124 {
125     BDRVBackupTopState *s = bs->opaque;
126
127     if (!s->active) {
128         /*
129          * The filter node may be in process of bdrv_append(), which firstly do
130          * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
131          * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
132          * let's require nothing during bdrv_append() and refresh permissions
133          * after it (see bdrv_backup_top_append()).
134          */
135         *nperm = 0;
136         *nshared = BLK_PERM_ALL;
137         return;
138     }
139
140     if (role == &child_file) {
141         /*
142          * Target child
143          *
144          * Share write to target (child_file), to not interfere
145          * with guest writes to its disk which may be in target backing chain.
146          */
147         *nshared = BLK_PERM_ALL;
148         *nperm = BLK_PERM_WRITE;
149     } else {
150         /* Source child */
151         bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
152                                   nperm, nshared);
153
154         if (perm & BLK_PERM_WRITE) {
155             *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
156         }
157         *nshared &= ~BLK_PERM_WRITE;
158     }
159 }
160
161 BlockDriver bdrv_backup_top_filter = {
162     .format_name = "backup-top",
163     .instance_size = sizeof(BDRVBackupTopState),
164
165     .bdrv_co_preadv             = backup_top_co_preadv,
166     .bdrv_co_pwritev            = backup_top_co_pwritev,
167     .bdrv_co_pwrite_zeroes      = backup_top_co_pwrite_zeroes,
168     .bdrv_co_pdiscard           = backup_top_co_pdiscard,
169     .bdrv_co_flush              = backup_top_co_flush,
170
171     .bdrv_co_block_status       = bdrv_co_block_status_from_backing,
172
173     .bdrv_refresh_filename      = backup_top_refresh_filename,
174
175     .bdrv_child_perm            = backup_top_child_perm,
176
177     .is_filter = true,
178 };
179
180 BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
181                                          BlockDriverState *target,
182                                          const char *filter_node_name,
183                                          uint64_t cluster_size,
184                                          BdrvRequestFlags write_flags,
185                                          BlockCopyState **bcs,
186                                          Error **errp)
187 {
188     Error *local_err = NULL;
189     BDRVBackupTopState *state;
190     BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
191                                                  filter_node_name,
192                                                  BDRV_O_RDWR, errp);
193
194     if (!top) {
195         return NULL;
196     }
197
198     top->total_sectors = source->total_sectors;
199     top->opaque = state = g_new0(BDRVBackupTopState, 1);
200
201     bdrv_ref(target);
202     state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
203     if (!state->target) {
204         bdrv_unref(target);
205         bdrv_unref(top);
206         return NULL;
207     }
208
209     bdrv_drained_begin(source);
210
211     bdrv_ref(top);
212     bdrv_append(top, source, &local_err);
213     if (local_err) {
214         error_prepend(&local_err, "Cannot append backup-top filter: ");
215         goto append_failed;
216     }
217
218     /*
219      * bdrv_append() finished successfully, now we can require permissions
220      * we want.
221      */
222     state->active = true;
223     bdrv_child_refresh_perms(top, top->backing, &local_err);
224     if (local_err) {
225         error_prepend(&local_err,
226                       "Cannot set permissions for backup-top filter: ");
227         goto failed_after_append;
228     }
229
230     state->bcs = block_copy_state_new(top->backing, state->target,
231                                       cluster_size, write_flags, &local_err);
232     if (local_err) {
233         error_prepend(&local_err, "Cannot create block-copy-state: ");
234         goto failed_after_append;
235     }
236     *bcs = state->bcs;
237
238     bdrv_drained_end(source);
239
240     return top;
241
242 failed_after_append:
243     state->active = false;
244     bdrv_backup_top_drop(top);
245
246 append_failed:
247     bdrv_drained_end(source);
248     bdrv_unref_child(top, state->target);
249     bdrv_unref(top);
250     error_propagate(errp, local_err);
251
252     return NULL;
253 }
254
255 void bdrv_backup_top_drop(BlockDriverState *bs)
256 {
257     BDRVBackupTopState *s = bs->opaque;
258     AioContext *aio_context = bdrv_get_aio_context(bs);
259
260     block_copy_state_free(s->bcs);
261
262     aio_context_acquire(aio_context);
263
264     bdrv_drained_begin(bs);
265
266     s->active = false;
267     bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
268     bdrv_replace_node(bs, backing_bs(bs), &error_abort);
269     bdrv_set_backing_hd(bs, NULL, &error_abort);
270
271     bdrv_drained_end(bs);
272
273     bdrv_unref(bs);
274
275     aio_context_release(aio_context);
276 }
This page took 0.036239 seconds and 4 git commands to generate.