qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at master 290 lines 8.8 kB view raw
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 <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/backup-top.h" 36 37typedef struct BDRVBackupTopState { 38 BlockCopyState *bcs; 39 BdrvChild *target; 40 bool active; 41 int64_t cluster_size; 42} BDRVBackupTopState; 43 44static 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 51static 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, NULL); 65} 66 67static 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 78static 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 89static 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 102static 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 111static 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 124static 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 168BlockDriver 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_co_block_status = bdrv_co_block_status_from_backing, 179 180 .bdrv_refresh_filename = backup_top_refresh_filename, 181 182 .bdrv_child_perm = backup_top_child_perm, 183 184 .is_filter = true, 185}; 186 187BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, 188 BlockDriverState *target, 189 const char *filter_node_name, 190 uint64_t cluster_size, 191 BdrvRequestFlags write_flags, 192 BlockCopyState **bcs, 193 Error **errp) 194{ 195 Error *local_err = NULL; 196 BDRVBackupTopState *state; 197 BlockDriverState *top; 198 bool appended = false; 199 200 assert(source->total_sectors == target->total_sectors); 201 202 top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name, 203 BDRV_O_RDWR, errp); 204 if (!top) { 205 return NULL; 206 } 207 208 state = top->opaque; 209 top->total_sectors = source->total_sectors; 210 top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | 211 (BDRV_REQ_FUA & source->supported_write_flags); 212 top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | 213 ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & 214 source->supported_zero_flags); 215 216 bdrv_ref(target); 217 state->target = bdrv_attach_child(top, target, "target", &child_of_bds, 218 BDRV_CHILD_DATA, errp); 219 if (!state->target) { 220 bdrv_unref(target); 221 bdrv_unref(top); 222 return NULL; 223 } 224 225 bdrv_drained_begin(source); 226 227 bdrv_ref(top); 228 bdrv_append(top, source, &local_err); 229 if (local_err) { 230 error_prepend(&local_err, "Cannot append backup-top filter: "); 231 goto fail; 232 } 233 appended = true; 234 235 /* 236 * bdrv_append() finished successfully, now we can require permissions 237 * we want. 238 */ 239 state->active = true; 240 bdrv_child_refresh_perms(top, top->backing, &local_err); 241 if (local_err) { 242 error_prepend(&local_err, 243 "Cannot set permissions for backup-top filter: "); 244 goto fail; 245 } 246 247 state->cluster_size = cluster_size; 248 state->bcs = block_copy_state_new(top->backing, state->target, 249 cluster_size, 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 260fail: 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 274void 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, backing_bs(bs), &error_abort); 285 bdrv_set_backing_hd(bs, NULL, &error_abort); 286 287 bdrv_drained_end(bs); 288 289 bdrv_unref(bs); 290}