Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 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 | |
| 37 | typedef struct BDRVBackupTopState { |
| 38 | BlockCopyState *bcs; |
| 39 | BdrvChild *target; |
| 40 | bool active; |
Vladimir Sementsov-Ogievskiy | 397f4e9 | 2020-03-11 13:30:04 +0300 | [diff] [blame] | 41 | int64_t cluster_size; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 42 | } BDRVBackupTopState; |
| 43 | |
| 44 | static 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 | |
| 51 | static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 52 | uint64_t bytes, BdrvRequestFlags flags) |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 53 | { |
Vladimir Sementsov-Ogievskiy | 00e30f0 | 2019-10-01 16:14:09 +0300 | [diff] [blame] | 54 | BDRVBackupTopState *s = bs->opaque; |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 55 | uint64_t off, end; |
| 56 | |
| 57 | if (flags & BDRV_REQ_WRITE_UNCHANGED) { |
| 58 | return 0; |
| 59 | } |
| 60 | |
Vladimir Sementsov-Ogievskiy | 397f4e9 | 2020-03-11 13:30:04 +0300 | [diff] [blame] | 61 | off = QEMU_ALIGN_DOWN(offset, s->cluster_size); |
| 62 | end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 63 | |
Vladimir Sementsov-Ogievskiy | 00e30f0 | 2019-10-01 16:14:09 +0300 | [diff] [blame] | 64 | return block_copy(s->bcs, off, end - off, NULL); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, |
| 68 | int64_t offset, int bytes) |
| 69 | { |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 70 | int ret = backup_top_cbw(bs, offset, bytes, 0); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 71 | if (ret < 0) { |
| 72 | return ret; |
| 73 | } |
| 74 | |
| 75 | return bdrv_co_pdiscard(bs->backing, offset, bytes); |
| 76 | } |
| 77 | |
| 78 | static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs, |
| 79 | int64_t offset, int bytes, BdrvRequestFlags flags) |
| 80 | { |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 81 | int ret = backup_top_cbw(bs, offset, bytes, flags); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 82 | if (ret < 0) { |
| 83 | return ret; |
| 84 | } |
| 85 | |
| 86 | return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); |
| 87 | } |
| 88 | |
| 89 | static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs, |
| 90 | uint64_t offset, |
| 91 | uint64_t bytes, |
| 92 | QEMUIOVector *qiov, int flags) |
| 93 | { |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 94 | int ret = backup_top_cbw(bs, offset, bytes, flags); |
| 95 | if (ret < 0) { |
| 96 | return ret; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); |
| 100 | } |
| 101 | |
| 102 | static 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 | |
| 111 | static 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 | |
| 124 | static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, |
Max Reitz | bf8e925 | 2020-05-13 13:05:16 +0200 | [diff] [blame] | 125 | BdrvChildRole role, |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 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 | |
Max Reitz | 25191e5 | 2020-05-13 13:05:33 +0200 | [diff] [blame] | 145 | if (!(role & BDRV_CHILD_FILTERED)) { |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 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. |
Kevin Wolf | 958a04b | 2020-04-30 16:27:54 +0200 | [diff] [blame] | 151 | * Can't resize during a backup block job because we check the size |
| 152 | * only upfront. |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 153 | */ |
Kevin Wolf | 958a04b | 2020-04-30 16:27:54 +0200 | [diff] [blame] | 154 | *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 155 | *nperm = BLK_PERM_WRITE; |
| 156 | } else { |
| 157 | /* Source child */ |
Max Reitz | e5d8a40 | 2020-05-13 13:05:44 +0200 | [diff] [blame] | 158 | bdrv_default_perms(bs, c, role, reopen_queue, |
Max Reitz | 69dca43 | 2020-05-13 13:05:39 +0200 | [diff] [blame] | 159 | perm, shared, nperm, nshared); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 160 | |
| 161 | if (perm & BLK_PERM_WRITE) { |
| 162 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; |
| 163 | } |
Kevin Wolf | 958a04b | 2020-04-30 16:27:54 +0200 | [diff] [blame] | 164 | *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 165 | } |
| 166 | } |
| 167 | |
| 168 | BlockDriver 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 | |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 178 | .bdrv_refresh_filename = backup_top_refresh_filename, |
| 179 | |
| 180 | .bdrv_child_perm = backup_top_child_perm, |
| 181 | |
| 182 | .is_filter = true, |
| 183 | }; |
| 184 | |
| 185 | BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, |
| 186 | BlockDriverState *target, |
| 187 | const char *filter_node_name, |
| 188 | uint64_t cluster_size, |
| 189 | BdrvRequestFlags write_flags, |
| 190 | BlockCopyState **bcs, |
| 191 | Error **errp) |
| 192 | { |
| 193 | Error *local_err = NULL; |
| 194 | BDRVBackupTopState *state; |
Kevin Wolf | 958a04b | 2020-04-30 16:27:54 +0200 | [diff] [blame] | 195 | BlockDriverState *top; |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 196 | bool appended = false; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 197 | |
Kevin Wolf | 958a04b | 2020-04-30 16:27:54 +0200 | [diff] [blame] | 198 | assert(source->total_sectors == target->total_sectors); |
| 199 | |
| 200 | top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name, |
| 201 | BDRV_O_RDWR, errp); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 202 | if (!top) { |
| 203 | return NULL; |
| 204 | } |
| 205 | |
Eiichi Tsukata | fb574de | 2019-12-23 18:06:32 +0900 | [diff] [blame] | 206 | state = top->opaque; |
Vladimir Sementsov-Ogievskiy | 4bc267a | 2020-02-07 19:12:31 +0300 | [diff] [blame] | 207 | top->total_sectors = source->total_sectors; |
| 208 | top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |
| 209 | (BDRV_REQ_FUA & source->supported_write_flags); |
| 210 | top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |
| 211 | ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & |
| 212 | source->supported_zero_flags); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 213 | |
| 214 | bdrv_ref(target); |
Max Reitz | 5894440 | 2020-05-13 13:05:37 +0200 | [diff] [blame] | 215 | state->target = bdrv_attach_child(top, target, "target", &child_of_bds, |
| 216 | BDRV_CHILD_DATA, errp); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 217 | if (!state->target) { |
| 218 | bdrv_unref(target); |
| 219 | bdrv_unref(top); |
| 220 | return NULL; |
| 221 | } |
| 222 | |
| 223 | bdrv_drained_begin(source); |
| 224 | |
| 225 | bdrv_ref(top); |
| 226 | bdrv_append(top, source, &local_err); |
| 227 | if (local_err) { |
| 228 | error_prepend(&local_err, "Cannot append backup-top filter: "); |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 229 | goto fail; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 230 | } |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 231 | appended = true; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 232 | |
| 233 | /* |
| 234 | * bdrv_append() finished successfully, now we can require permissions |
| 235 | * we want. |
| 236 | */ |
| 237 | state->active = true; |
| 238 | bdrv_child_refresh_perms(top, top->backing, &local_err); |
| 239 | if (local_err) { |
| 240 | error_prepend(&local_err, |
| 241 | "Cannot set permissions for backup-top filter: "); |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 242 | goto fail; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 243 | } |
| 244 | |
Vladimir Sementsov-Ogievskiy | 397f4e9 | 2020-03-11 13:30:04 +0300 | [diff] [blame] | 245 | state->cluster_size = cluster_size; |
Vladimir Sementsov-Ogievskiy | 00e30f0 | 2019-10-01 16:14:09 +0300 | [diff] [blame] | 246 | state->bcs = block_copy_state_new(top->backing, state->target, |
| 247 | cluster_size, write_flags, &local_err); |
| 248 | if (local_err) { |
| 249 | error_prepend(&local_err, "Cannot create block-copy-state: "); |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 250 | goto fail; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 251 | } |
| 252 | *bcs = state->bcs; |
| 253 | |
| 254 | bdrv_drained_end(source); |
| 255 | |
| 256 | return top; |
| 257 | |
Vladimir Sementsov-Ogievskiy | 0df62f4 | 2020-01-21 17:28:01 +0300 | [diff] [blame] | 258 | fail: |
| 259 | if (appended) { |
| 260 | state->active = false; |
| 261 | bdrv_backup_top_drop(top); |
| 262 | } else { |
| 263 | bdrv_unref(top); |
| 264 | } |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 265 | |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 266 | bdrv_drained_end(source); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 267 | error_propagate(errp, local_err); |
| 268 | |
| 269 | return NULL; |
| 270 | } |
| 271 | |
| 272 | void bdrv_backup_top_drop(BlockDriverState *bs) |
| 273 | { |
| 274 | BDRVBackupTopState *s = bs->opaque; |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 275 | |
| 276 | bdrv_drained_begin(bs); |
| 277 | |
Max Reitz | 503ca12 | 2019-12-19 19:26:38 +0100 | [diff] [blame] | 278 | block_copy_state_free(s->bcs); |
| 279 | |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 280 | s->active = false; |
| 281 | bdrv_child_refresh_perms(bs, bs->backing, &error_abort); |
Max Reitz | 2b088c6 | 2019-06-12 17:46:45 +0200 | [diff] [blame] | 282 | bdrv_replace_node(bs, bs->backing->bs, &error_abort); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 283 | bdrv_set_backing_hd(bs, NULL, &error_abort); |
| 284 | |
| 285 | bdrv_drained_end(bs); |
| 286 | |
| 287 | bdrv_unref(bs); |
Vladimir Sementsov-Ogievskiy | 7df7868 | 2019-10-01 16:14:08 +0300 | [diff] [blame] | 288 | } |