copy-before-write.c (7746B)
1/* 2 * copy-before-write 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-2021 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/copy-before-write.h" 36 37typedef struct BDRVCopyBeforeWriteState { 38 BlockCopyState *bcs; 39 BdrvChild *target; 40} BDRVCopyBeforeWriteState; 41 42static coroutine_fn int cbw_co_preadv( 43 BlockDriverState *bs, int64_t offset, int64_t bytes, 44 QEMUIOVector *qiov, BdrvRequestFlags flags) 45{ 46 return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); 47} 48 49static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, 50 uint64_t offset, uint64_t bytes, BdrvRequestFlags flags) 51{ 52 BDRVCopyBeforeWriteState *s = bs->opaque; 53 uint64_t off, end; 54 int64_t cluster_size = block_copy_cluster_size(s->bcs); 55 56 if (flags & BDRV_REQ_WRITE_UNCHANGED) { 57 return 0; 58 } 59 60 off = QEMU_ALIGN_DOWN(offset, cluster_size); 61 end = QEMU_ALIGN_UP(offset + bytes, cluster_size); 62 63 return block_copy(s->bcs, off, end - off, true); 64} 65 66static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, 67 int64_t offset, int64_t bytes) 68{ 69 int ret = cbw_do_copy_before_write(bs, offset, bytes, 0); 70 if (ret < 0) { 71 return ret; 72 } 73 74 return bdrv_co_pdiscard(bs->file, offset, bytes); 75} 76 77static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, 78 int64_t offset, int64_t bytes, BdrvRequestFlags flags) 79{ 80 int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); 81 if (ret < 0) { 82 return ret; 83 } 84 85 return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); 86} 87 88static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, 89 int64_t offset, 90 int64_t bytes, 91 QEMUIOVector *qiov, 92 BdrvRequestFlags flags) 93{ 94 int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); 95 if (ret < 0) { 96 return ret; 97 } 98 99 return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); 100} 101 102static int coroutine_fn cbw_co_flush(BlockDriverState *bs) 103{ 104 if (!bs->file) { 105 return 0; 106 } 107 108 return bdrv_co_flush(bs->file->bs); 109} 110 111static void cbw_refresh_filename(BlockDriverState *bs) 112{ 113 pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), 114 bs->file->bs->filename); 115} 116 117static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, 118 BdrvChildRole role, 119 BlockReopenQueue *reopen_queue, 120 uint64_t perm, uint64_t shared, 121 uint64_t *nperm, uint64_t *nshared) 122{ 123 if (!(role & BDRV_CHILD_FILTERED)) { 124 /* 125 * Target child 126 * 127 * Share write to target (child_file), to not interfere 128 * with guest writes to its disk which may be in target backing chain. 129 * Can't resize during a backup block job because we check the size 130 * only upfront. 131 */ 132 *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; 133 *nperm = BLK_PERM_WRITE; 134 } else { 135 /* Source child */ 136 bdrv_default_perms(bs, c, role, reopen_queue, 137 perm, shared, nperm, nshared); 138 139 if (!QLIST_EMPTY(&bs->parents)) { 140 if (perm & BLK_PERM_WRITE) { 141 *nperm = *nperm | BLK_PERM_CONSISTENT_READ; 142 } 143 *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); 144 } 145 } 146} 147 148static int cbw_open(BlockDriverState *bs, QDict *options, int flags, 149 Error **errp) 150{ 151 BDRVCopyBeforeWriteState *s = bs->opaque; 152 BdrvDirtyBitmap *copy_bitmap; 153 154 bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, 155 BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, 156 false, errp); 157 if (!bs->file) { 158 return -EINVAL; 159 } 160 161 s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds, 162 BDRV_CHILD_DATA, false, errp); 163 if (!s->target) { 164 return -EINVAL; 165 } 166 167 bs->total_sectors = bs->file->bs->total_sectors; 168 bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | 169 (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); 170 bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | 171 ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & 172 bs->file->bs->supported_zero_flags); 173 174 s->bcs = block_copy_state_new(bs->file, s->target, errp); 175 if (!s->bcs) { 176 error_prepend(errp, "Cannot create block-copy-state: "); 177 return -EINVAL; 178 } 179 180 copy_bitmap = block_copy_dirty_bitmap(s->bcs); 181 bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap)); 182 183 return 0; 184} 185 186static void cbw_close(BlockDriverState *bs) 187{ 188 BDRVCopyBeforeWriteState *s = bs->opaque; 189 190 block_copy_state_free(s->bcs); 191 s->bcs = NULL; 192} 193 194BlockDriver bdrv_cbw_filter = { 195 .format_name = "copy-before-write", 196 .instance_size = sizeof(BDRVCopyBeforeWriteState), 197 198 .bdrv_open = cbw_open, 199 .bdrv_close = cbw_close, 200 201 .bdrv_co_preadv = cbw_co_preadv, 202 .bdrv_co_pwritev = cbw_co_pwritev, 203 .bdrv_co_pwrite_zeroes = cbw_co_pwrite_zeroes, 204 .bdrv_co_pdiscard = cbw_co_pdiscard, 205 .bdrv_co_flush = cbw_co_flush, 206 207 .bdrv_refresh_filename = cbw_refresh_filename, 208 209 .bdrv_child_perm = cbw_child_perm, 210 211 .is_filter = true, 212}; 213 214BlockDriverState *bdrv_cbw_append(BlockDriverState *source, 215 BlockDriverState *target, 216 const char *filter_node_name, 217 BlockCopyState **bcs, 218 Error **errp) 219{ 220 ERRP_GUARD(); 221 BDRVCopyBeforeWriteState *state; 222 BlockDriverState *top; 223 QDict *opts; 224 225 assert(source->total_sectors == target->total_sectors); 226 227 opts = qdict_new(); 228 qdict_put_str(opts, "driver", "copy-before-write"); 229 if (filter_node_name) { 230 qdict_put_str(opts, "node-name", filter_node_name); 231 } 232 qdict_put_str(opts, "file", bdrv_get_node_name(source)); 233 qdict_put_str(opts, "target", bdrv_get_node_name(target)); 234 235 top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp); 236 if (!top) { 237 return NULL; 238 } 239 240 state = top->opaque; 241 *bcs = state->bcs; 242 243 return top; 244} 245 246void bdrv_cbw_drop(BlockDriverState *bs) 247{ 248 bdrv_drop_filter(bs, &error_abort); 249 bdrv_unref(bs); 250} 251 252static void cbw_init(void) 253{ 254 bdrv_register(&bdrv_cbw_filter); 255} 256 257block_init(cbw_init);