1 /* 2 * Block protocol for block driver correctness testing 3 * 4 * Copyright (C) 2010 IBM, Corp. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include <stdarg.h> 11 #include "qemu/sockets.h" /* for EINPROGRESS on Windows */ 12 #include "block/block_int.h" 13 14 typedef struct { 15 BlockDriverState *test_file; 16 } BDRVBlkverifyState; 17 18 typedef struct BlkverifyAIOCB BlkverifyAIOCB; 19 struct BlkverifyAIOCB { 20 BlockDriverAIOCB common; 21 QEMUBH *bh; 22 23 /* Request metadata */ 24 bool is_write; 25 int64_t sector_num; 26 int nb_sectors; 27 28 int ret; /* first completed request's result */ 29 unsigned int done; /* completion counter */ 30 bool *finished; /* completion signal for cancel */ 31 32 QEMUIOVector *qiov; /* user I/O vector */ 33 QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */ 34 void *buf; /* buffer for raw file I/O */ 35 36 void (*verify)(BlkverifyAIOCB *acb); 37 }; 38 39 static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb) 40 { 41 BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb; 42 bool finished = false; 43 44 /* Wait until request completes, invokes its callback, and frees itself */ 45 acb->finished = &finished; 46 while (!finished) { 47 qemu_aio_wait(); 48 } 49 } 50 51 static const AIOCBInfo blkverify_aiocb_info = { 52 .aiocb_size = sizeof(BlkverifyAIOCB), 53 .cancel = blkverify_aio_cancel, 54 }; 55 56 static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, 57 const char *fmt, ...) 58 { 59 va_list ap; 60 61 va_start(ap, fmt); 62 fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", 63 acb->is_write ? "write" : "read", acb->sector_num, 64 acb->nb_sectors); 65 vfprintf(stderr, fmt, ap); 66 fprintf(stderr, "\n"); 67 va_end(ap); 68 exit(1); 69 } 70 71 /* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ 72 static void blkverify_parse_filename(const char *filename, QDict *options, 73 Error **errp) 74 { 75 const char *c; 76 QString *raw_path; 77 78 79 /* Parse the blkverify: prefix */ 80 if (!strstart(filename, "blkverify:", &filename)) { 81 /* There was no prefix; therefore, all options have to be already 82 present in the QDict (except for the filename) */ 83 qdict_put(options, "x-image", qstring_from_str(filename)); 84 return; 85 } 86 87 /* Parse the raw image filename */ 88 c = strchr(filename, ':'); 89 if (c == NULL) { 90 error_setg(errp, "blkverify requires raw copy and original image path"); 91 return; 92 } 93 94 /* TODO Implement option pass-through and set raw.filename here */ 95 raw_path = qstring_from_substr(filename, 0, c - filename - 1); 96 qdict_put(options, "x-raw", raw_path); 97 98 /* TODO Allow multi-level nesting and set file.filename here */ 99 filename = c + 1; 100 qdict_put(options, "x-image", qstring_from_str(filename)); 101 } 102 103 static QemuOptsList runtime_opts = { 104 .name = "blkverify", 105 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 106 .desc = { 107 { 108 .name = "x-raw", 109 .type = QEMU_OPT_STRING, 110 .help = "[internal use only, will be removed]", 111 }, 112 { 113 .name = "x-image", 114 .type = QEMU_OPT_STRING, 115 .help = "[internal use only, will be removed]", 116 }, 117 { /* end of list */ } 118 }, 119 }; 120 121 static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, 122 Error **errp) 123 { 124 BDRVBlkverifyState *s = bs->opaque; 125 QemuOpts *opts; 126 Error *local_err = NULL; 127 int ret; 128 129 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 130 qemu_opts_absorb_qdict(opts, options, &local_err); 131 if (local_err) { 132 error_propagate(errp, local_err); 133 ret = -EINVAL; 134 goto fail; 135 } 136 137 /* Open the raw file */ 138 assert(bs->file == NULL); 139 ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, 140 "raw", flags | BDRV_O_PROTOCOL, false, &local_err); 141 if (ret < 0) { 142 error_propagate(errp, local_err); 143 goto fail; 144 } 145 146 /* Open the test file */ 147 assert(s->test_file == NULL); 148 ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, 149 "test", flags, false, &local_err); 150 if (ret < 0) { 151 error_propagate(errp, local_err); 152 s->test_file = NULL; 153 goto fail; 154 } 155 156 ret = 0; 157 fail: 158 return ret; 159 } 160 161 static void blkverify_close(BlockDriverState *bs) 162 { 163 BDRVBlkverifyState *s = bs->opaque; 164 165 bdrv_unref(s->test_file); 166 s->test_file = NULL; 167 } 168 169 static int64_t blkverify_getlength(BlockDriverState *bs) 170 { 171 BDRVBlkverifyState *s = bs->opaque; 172 173 return bdrv_getlength(s->test_file); 174 } 175 176 static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, 177 int64_t sector_num, QEMUIOVector *qiov, 178 int nb_sectors, 179 BlockDriverCompletionFunc *cb, 180 void *opaque) 181 { 182 BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aiocb_info, bs, cb, opaque); 183 184 acb->bh = NULL; 185 acb->is_write = is_write; 186 acb->sector_num = sector_num; 187 acb->nb_sectors = nb_sectors; 188 acb->ret = -EINPROGRESS; 189 acb->done = 0; 190 acb->qiov = qiov; 191 acb->buf = NULL; 192 acb->verify = NULL; 193 acb->finished = NULL; 194 return acb; 195 } 196 197 static void blkverify_aio_bh(void *opaque) 198 { 199 BlkverifyAIOCB *acb = opaque; 200 201 qemu_bh_delete(acb->bh); 202 if (acb->buf) { 203 qemu_iovec_destroy(&acb->raw_qiov); 204 qemu_vfree(acb->buf); 205 } 206 acb->common.cb(acb->common.opaque, acb->ret); 207 if (acb->finished) { 208 *acb->finished = true; 209 } 210 qemu_aio_release(acb); 211 } 212 213 static void blkverify_aio_cb(void *opaque, int ret) 214 { 215 BlkverifyAIOCB *acb = opaque; 216 217 switch (++acb->done) { 218 case 1: 219 acb->ret = ret; 220 break; 221 222 case 2: 223 if (acb->ret != ret) { 224 blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); 225 } 226 227 if (acb->verify) { 228 acb->verify(acb); 229 } 230 231 acb->bh = qemu_bh_new(blkverify_aio_bh, acb); 232 qemu_bh_schedule(acb->bh); 233 break; 234 } 235 } 236 237 static void blkverify_verify_readv(BlkverifyAIOCB *acb) 238 { 239 ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov); 240 if (offset != -1) { 241 blkverify_err(acb, "contents mismatch in sector %" PRId64, 242 acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); 243 } 244 } 245 246 static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, 247 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, 248 BlockDriverCompletionFunc *cb, void *opaque) 249 { 250 BDRVBlkverifyState *s = bs->opaque; 251 BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, 252 nb_sectors, cb, opaque); 253 254 acb->verify = blkverify_verify_readv; 255 acb->buf = qemu_blockalign(bs->file, qiov->size); 256 qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); 257 qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); 258 259 bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, 260 blkverify_aio_cb, acb); 261 bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, 262 blkverify_aio_cb, acb); 263 return &acb->common; 264 } 265 266 static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs, 267 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, 268 BlockDriverCompletionFunc *cb, void *opaque) 269 { 270 BDRVBlkverifyState *s = bs->opaque; 271 BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, 272 nb_sectors, cb, opaque); 273 274 bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, 275 blkverify_aio_cb, acb); 276 bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, 277 blkverify_aio_cb, acb); 278 return &acb->common; 279 } 280 281 static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs, 282 BlockDriverCompletionFunc *cb, 283 void *opaque) 284 { 285 BDRVBlkverifyState *s = bs->opaque; 286 287 /* Only flush test file, the raw file is not important */ 288 return bdrv_aio_flush(s->test_file, cb, opaque); 289 } 290 291 static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, 292 BlockDriverState *candidate) 293 { 294 BDRVBlkverifyState *s = bs->opaque; 295 296 bool perm = bdrv_recurse_is_first_non_filter(bs->file, candidate); 297 298 if (perm) { 299 return true; 300 } 301 302 return bdrv_recurse_is_first_non_filter(s->test_file, candidate); 303 } 304 305 static BlockDriver bdrv_blkverify = { 306 .format_name = "blkverify", 307 .protocol_name = "blkverify", 308 .instance_size = sizeof(BDRVBlkverifyState), 309 310 .bdrv_parse_filename = blkverify_parse_filename, 311 .bdrv_file_open = blkverify_open, 312 .bdrv_close = blkverify_close, 313 .bdrv_getlength = blkverify_getlength, 314 315 .bdrv_aio_readv = blkverify_aio_readv, 316 .bdrv_aio_writev = blkverify_aio_writev, 317 .bdrv_aio_flush = blkverify_aio_flush, 318 319 .is_filter = true, 320 .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter, 321 }; 322 323 static void bdrv_blkverify_init(void) 324 { 325 bdrv_register(&bdrv_blkverify); 326 } 327 328 block_init(bdrv_blkverify_init); 329