2 * copy-before-write filter driver
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.
7 * Copyright (c) 2018-2021 Virtuozzo International GmbH.
10 * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
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.
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.
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/>.
26 #include "qemu/osdep.h"
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"
35 #include "block/copy-before-write.h"
37 typedef struct BDRVCopyBeforeWriteState
{
40 } BDRVCopyBeforeWriteState
;
42 static coroutine_fn
int cbw_co_preadv(
43 BlockDriverState
*bs
, uint64_t offset
, uint64_t bytes
,
44 QEMUIOVector
*qiov
, int flags
)
46 return bdrv_co_preadv(bs
->file
, offset
, bytes
, qiov
, flags
);
49 static coroutine_fn
int cbw_do_copy_before_write(BlockDriverState
*bs
,
50 uint64_t offset
, uint64_t bytes
, BdrvRequestFlags flags
)
52 BDRVCopyBeforeWriteState
*s
= bs
->opaque
;
54 int64_t cluster_size
= block_copy_cluster_size(s
->bcs
);
56 if (flags
& BDRV_REQ_WRITE_UNCHANGED
) {
60 off
= QEMU_ALIGN_DOWN(offset
, cluster_size
);
61 end
= QEMU_ALIGN_UP(offset
+ bytes
, cluster_size
);
63 return block_copy(s
->bcs
, off
, end
- off
, true);
66 static int coroutine_fn
cbw_co_pdiscard(BlockDriverState
*bs
,
67 int64_t offset
, int bytes
)
69 int ret
= cbw_do_copy_before_write(bs
, offset
, bytes
, 0);
74 return bdrv_co_pdiscard(bs
->file
, offset
, bytes
);
77 static int coroutine_fn
cbw_co_pwrite_zeroes(BlockDriverState
*bs
,
78 int64_t offset
, int bytes
, BdrvRequestFlags flags
)
80 int ret
= cbw_do_copy_before_write(bs
, offset
, bytes
, flags
);
85 return bdrv_co_pwrite_zeroes(bs
->file
, offset
, bytes
, flags
);
88 static coroutine_fn
int cbw_co_pwritev(BlockDriverState
*bs
,
91 QEMUIOVector
*qiov
, int flags
)
93 int ret
= cbw_do_copy_before_write(bs
, offset
, bytes
, flags
);
98 return bdrv_co_pwritev(bs
->file
, offset
, bytes
, qiov
, flags
);
101 static int coroutine_fn
cbw_co_flush(BlockDriverState
*bs
)
107 return bdrv_co_flush(bs
->file
->bs
);
110 static void cbw_refresh_filename(BlockDriverState
*bs
)
112 pstrcpy(bs
->exact_filename
, sizeof(bs
->exact_filename
),
113 bs
->file
->bs
->filename
);
116 static void cbw_child_perm(BlockDriverState
*bs
, BdrvChild
*c
,
118 BlockReopenQueue
*reopen_queue
,
119 uint64_t perm
, uint64_t shared
,
120 uint64_t *nperm
, uint64_t *nshared
)
122 if (!(role
& BDRV_CHILD_FILTERED
)) {
126 * Share write to target (child_file), to not interfere
127 * with guest writes to its disk which may be in target backing chain.
128 * Can't resize during a backup block job because we check the size
131 *nshared
= BLK_PERM_ALL
& ~BLK_PERM_RESIZE
;
132 *nperm
= BLK_PERM_WRITE
;
135 bdrv_default_perms(bs
, c
, role
, reopen_queue
,
136 perm
, shared
, nperm
, nshared
);
138 if (!QLIST_EMPTY(&bs
->parents
)) {
139 if (perm
& BLK_PERM_WRITE
) {
140 *nperm
= *nperm
| BLK_PERM_CONSISTENT_READ
;
142 *nshared
&= ~(BLK_PERM_WRITE
| BLK_PERM_RESIZE
);
147 static int cbw_open(BlockDriverState
*bs
, QDict
*options
, int flags
,
150 BDRVCopyBeforeWriteState
*s
= bs
->opaque
;
151 BdrvDirtyBitmap
*copy_bitmap
;
153 bs
->file
= bdrv_open_child(NULL
, options
, "file", bs
, &child_of_bds
,
154 BDRV_CHILD_FILTERED
| BDRV_CHILD_PRIMARY
,
160 s
->target
= bdrv_open_child(NULL
, options
, "target", bs
, &child_of_bds
,
161 BDRV_CHILD_DATA
, false, errp
);
166 bs
->total_sectors
= bs
->file
->bs
->total_sectors
;
167 bs
->supported_write_flags
= BDRV_REQ_WRITE_UNCHANGED
|
168 (BDRV_REQ_FUA
& bs
->file
->bs
->supported_write_flags
);
169 bs
->supported_zero_flags
= BDRV_REQ_WRITE_UNCHANGED
|
170 ((BDRV_REQ_FUA
| BDRV_REQ_MAY_UNMAP
| BDRV_REQ_NO_FALLBACK
) &
171 bs
->file
->bs
->supported_zero_flags
);
173 s
->bcs
= block_copy_state_new(bs
->file
, s
->target
, errp
);
175 error_prepend(errp
, "Cannot create block-copy-state: ");
179 copy_bitmap
= block_copy_dirty_bitmap(s
->bcs
);
180 bdrv_set_dirty_bitmap(copy_bitmap
, 0, bdrv_dirty_bitmap_size(copy_bitmap
));
185 static void cbw_close(BlockDriverState
*bs
)
187 BDRVCopyBeforeWriteState
*s
= bs
->opaque
;
189 block_copy_state_free(s
->bcs
);
193 BlockDriver bdrv_cbw_filter
= {
194 .format_name
= "copy-before-write",
195 .instance_size
= sizeof(BDRVCopyBeforeWriteState
),
197 .bdrv_open
= cbw_open
,
198 .bdrv_close
= cbw_close
,
200 .bdrv_co_preadv
= cbw_co_preadv
,
201 .bdrv_co_pwritev
= cbw_co_pwritev
,
202 .bdrv_co_pwrite_zeroes
= cbw_co_pwrite_zeroes
,
203 .bdrv_co_pdiscard
= cbw_co_pdiscard
,
204 .bdrv_co_flush
= cbw_co_flush
,
206 .bdrv_refresh_filename
= cbw_refresh_filename
,
208 .bdrv_child_perm
= cbw_child_perm
,
213 BlockDriverState
*bdrv_cbw_append(BlockDriverState
*source
,
214 BlockDriverState
*target
,
215 const char *filter_node_name
,
216 BlockCopyState
**bcs
,
220 BDRVCopyBeforeWriteState
*state
;
221 BlockDriverState
*top
;
224 assert(source
->total_sectors
== target
->total_sectors
);
227 qdict_put_str(opts
, "driver", "copy-before-write");
228 if (filter_node_name
) {
229 qdict_put_str(opts
, "node-name", filter_node_name
);
231 qdict_put_str(opts
, "file", bdrv_get_node_name(source
));
232 qdict_put_str(opts
, "target", bdrv_get_node_name(target
));
234 top
= bdrv_insert_node(source
, opts
, BDRV_O_RDWR
, errp
);
245 void bdrv_cbw_drop(BlockDriverState
*bs
)
247 bdrv_drop_filter(bs
, &error_abort
);
251 static void cbw_init(void)
253 bdrv_register(&bdrv_cbw_filter
);
256 block_init(cbw_init
);