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 37 typedef struct BDRVCopyBeforeWriteState { 38 BlockCopyState *bcs; 39 BdrvChild *target; 40 } BDRVCopyBeforeWriteState; 41 42 static coroutine_fn int cbw_co_preadv( 43 BlockDriverState *bs, uint64_t offset, uint64_t bytes, 44 QEMUIOVector *qiov, int flags) 45 { 46 return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); 47 } 48 49 static 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 66 static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs, 67 int64_t offset, int 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 77 static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs, 78 int64_t offset, int 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 88 static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs, 89 uint64_t offset, 90 uint64_t bytes, 91 QEMUIOVector *qiov, int flags) 92 { 93 int ret = cbw_do_copy_before_write(bs, offset, bytes, flags); 94 if (ret < 0) { 95 return ret; 96 } 97 98 return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); 99 } 100 101 static int coroutine_fn cbw_co_flush(BlockDriverState *bs) 102 { 103 if (!bs->file) { 104 return 0; 105 } 106 107 return bdrv_co_flush(bs->file->bs); 108 } 109 110 static void cbw_refresh_filename(BlockDriverState *bs) 111 { 112 pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), 113 bs->file->bs->filename); 114 } 115 116 static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, 117 BdrvChildRole role, 118 BlockReopenQueue *reopen_queue, 119 uint64_t perm, uint64_t shared, 120 uint64_t *nperm, uint64_t *nshared) 121 { 122 if (!(role & BDRV_CHILD_FILTERED)) { 123 /* 124 * Target child 125 * 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 129 * only upfront. 130 */ 131 *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; 132 *nperm = BLK_PERM_WRITE; 133 } else { 134 /* Source child */ 135 bdrv_default_perms(bs, c, role, reopen_queue, 136 perm, shared, nperm, nshared); 137 138 if (!QLIST_EMPTY(&bs->parents)) { 139 if (perm & BLK_PERM_WRITE) { 140 *nperm = *nperm | BLK_PERM_CONSISTENT_READ; 141 } 142 *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); 143 } 144 } 145 } 146 147 static int cbw_open(BlockDriverState *bs, QDict *options, int flags, 148 Error **errp) 149 { 150 BDRVCopyBeforeWriteState *s = bs->opaque; 151 BdrvDirtyBitmap *copy_bitmap; 152 153 bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, 154 BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, 155 false, errp); 156 if (!bs->file) { 157 return -EINVAL; 158 } 159 160 s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds, 161 BDRV_CHILD_DATA, false, errp); 162 if (!s->target) { 163 return -EINVAL; 164 } 165 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); 172 173 s->bcs = block_copy_state_new(bs->file, s->target, errp); 174 if (!s->bcs) { 175 error_prepend(errp, "Cannot create block-copy-state: "); 176 return -EINVAL; 177 } 178 179 copy_bitmap = block_copy_dirty_bitmap(s->bcs); 180 bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap)); 181 182 return 0; 183 } 184 185 static void cbw_close(BlockDriverState *bs) 186 { 187 BDRVCopyBeforeWriteState *s = bs->opaque; 188 189 block_copy_state_free(s->bcs); 190 s->bcs = NULL; 191 } 192 193 BlockDriver bdrv_cbw_filter = { 194 .format_name = "copy-before-write", 195 .instance_size = sizeof(BDRVCopyBeforeWriteState), 196 197 .bdrv_open = cbw_open, 198 .bdrv_close = cbw_close, 199 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, 205 206 .bdrv_refresh_filename = cbw_refresh_filename, 207 208 .bdrv_child_perm = cbw_child_perm, 209 210 .is_filter = true, 211 }; 212 213 BlockDriverState *bdrv_cbw_append(BlockDriverState *source, 214 BlockDriverState *target, 215 const char *filter_node_name, 216 BlockCopyState **bcs, 217 Error **errp) 218 { 219 ERRP_GUARD(); 220 BDRVCopyBeforeWriteState *state; 221 BlockDriverState *top; 222 QDict *opts; 223 224 assert(source->total_sectors == target->total_sectors); 225 226 opts = qdict_new(); 227 qdict_put_str(opts, "driver", "copy-before-write"); 228 if (filter_node_name) { 229 qdict_put_str(opts, "node-name", filter_node_name); 230 } 231 qdict_put_str(opts, "file", bdrv_get_node_name(source)); 232 qdict_put_str(opts, "target", bdrv_get_node_name(target)); 233 234 top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp); 235 if (!top) { 236 return NULL; 237 } 238 239 state = top->opaque; 240 *bcs = state->bcs; 241 242 return top; 243 } 244 245 void bdrv_cbw_drop(BlockDriverState *bs) 246 { 247 bdrv_drop_filter(bs, &error_abort); 248 bdrv_unref(bs); 249 } 250 251 static void cbw_init(void) 252 { 253 bdrv_register(&bdrv_cbw_filter); 254 } 255 256 block_init(cbw_init); 257