qemu/block/blkverify.c
<<
>>
Prefs
   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_socket.h" /* for EINPROGRESS on Windows */
  12#include "block_int.h"
  13
  14typedef struct {
  15    BlockDriverState *test_file;
  16} BDRVBlkverifyState;
  17
  18typedef struct BlkverifyAIOCB BlkverifyAIOCB;
  19struct 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
  39static 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
  51static AIOPool blkverify_aio_pool = {
  52    .aiocb_size         = sizeof(BlkverifyAIOCB),
  53    .cancel             = blkverify_aio_cancel,
  54};
  55
  56static 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 */
  72static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
  73{
  74    BDRVBlkverifyState *s = bs->opaque;
  75    int ret;
  76    char *raw, *c;
  77
  78    /* Parse the blkverify: prefix */
  79    if (strncmp(filename, "blkverify:", strlen("blkverify:"))) {
  80        return -EINVAL;
  81    }
  82    filename += strlen("blkverify:");
  83
  84    /* Parse the raw image filename */
  85    c = strchr(filename, ':');
  86    if (c == NULL) {
  87        return -EINVAL;
  88    }
  89
  90    raw = strdup(filename);
  91    raw[c - filename] = '\0';
  92    ret = bdrv_file_open(&bs->file, raw, flags);
  93    free(raw);
  94    if (ret < 0) {
  95        return ret;
  96    }
  97    filename = c + 1;
  98
  99    /* Open the test file */
 100    s->test_file = bdrv_new("");
 101    ret = bdrv_open(s->test_file, filename, flags, NULL);
 102    if (ret < 0) {
 103        bdrv_delete(s->test_file);
 104        s->test_file = NULL;
 105        return ret;
 106    }
 107
 108    return 0;
 109}
 110
 111static void blkverify_close(BlockDriverState *bs)
 112{
 113    BDRVBlkverifyState *s = bs->opaque;
 114
 115    bdrv_delete(s->test_file);
 116    s->test_file = NULL;
 117}
 118
 119static int blkverify_flush(BlockDriverState *bs)
 120{
 121    BDRVBlkverifyState *s = bs->opaque;
 122
 123    /* Only flush test file, the raw file is not important */
 124    return bdrv_flush(s->test_file);
 125}
 126
 127static int64_t blkverify_getlength(BlockDriverState *bs)
 128{
 129    BDRVBlkverifyState *s = bs->opaque;
 130
 131    return bdrv_getlength(s->test_file);
 132}
 133
 134/**
 135 * Check that I/O vector contents are identical
 136 *
 137 * @a:          I/O vector
 138 * @b:          I/O vector
 139 * @ret:        Offset to first mismatching byte or -1 if match
 140 */
 141static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
 142{
 143    int i;
 144    ssize_t offset = 0;
 145
 146    assert(a->niov == b->niov);
 147    for (i = 0; i < a->niov; i++) {
 148        size_t len = 0;
 149        uint8_t *p = (uint8_t *)a->iov[i].iov_base;
 150        uint8_t *q = (uint8_t *)b->iov[i].iov_base;
 151
 152        assert(a->iov[i].iov_len == b->iov[i].iov_len);
 153        while (len < a->iov[i].iov_len && *p++ == *q++) {
 154            len++;
 155        }
 156
 157        offset += len;
 158
 159        if (len != a->iov[i].iov_len) {
 160            return offset;
 161        }
 162    }
 163    return -1;
 164}
 165
 166typedef struct {
 167    int src_index;
 168    struct iovec *src_iov;
 169    void *dest_base;
 170} IOVectorSortElem;
 171
 172static int sortelem_cmp_src_base(const void *a, const void *b)
 173{
 174    const IOVectorSortElem *elem_a = a;
 175    const IOVectorSortElem *elem_b = b;
 176
 177    /* Don't overflow */
 178    if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
 179        return -1;
 180    } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
 181        return 1;
 182    } else {
 183        return 0;
 184    }
 185}
 186
 187static int sortelem_cmp_src_index(const void *a, const void *b)
 188{
 189    const IOVectorSortElem *elem_a = a;
 190    const IOVectorSortElem *elem_b = b;
 191
 192    return elem_a->src_index - elem_b->src_index;
 193}
 194
 195/**
 196 * Copy contents of I/O vector
 197 *
 198 * The relative relationships of overlapping iovecs are preserved.  This is
 199 * necessary to ensure identical semantics in the cloned I/O vector.
 200 */
 201static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
 202                                  void *buf)
 203{
 204    IOVectorSortElem sortelems[src->niov];
 205    void *last_end;
 206    int i;
 207
 208    /* Sort by source iovecs by base address */
 209    for (i = 0; i < src->niov; i++) {
 210        sortelems[i].src_index = i;
 211        sortelems[i].src_iov = &src->iov[i];
 212    }
 213    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
 214
 215    /* Allocate buffer space taking into account overlapping iovecs */
 216    last_end = NULL;
 217    for (i = 0; i < src->niov; i++) {
 218        struct iovec *cur = sortelems[i].src_iov;
 219        ptrdiff_t rewind = 0;
 220
 221        /* Detect overlap */
 222        if (last_end && last_end > cur->iov_base) {
 223            rewind = last_end - cur->iov_base;
 224        }
 225
 226        sortelems[i].dest_base = buf - rewind;
 227        buf += cur->iov_len - MIN(rewind, cur->iov_len);
 228        last_end = MAX(cur->iov_base + cur->iov_len, last_end);
 229    }
 230
 231    /* Sort by source iovec index and build destination iovec */
 232    qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
 233    for (i = 0; i < src->niov; i++) {
 234        qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
 235    }
 236}
 237
 238static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
 239                                         int64_t sector_num, QEMUIOVector *qiov,
 240                                         int nb_sectors,
 241                                         BlockDriverCompletionFunc *cb,
 242                                         void *opaque)
 243{
 244    BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque);
 245
 246    acb->bh = NULL;
 247    acb->is_write = is_write;
 248    acb->sector_num = sector_num;
 249    acb->nb_sectors = nb_sectors;
 250    acb->ret = -EINPROGRESS;
 251    acb->done = 0;
 252    acb->qiov = qiov;
 253    acb->buf = NULL;
 254    acb->verify = NULL;
 255    acb->finished = NULL;
 256    return acb;
 257}
 258
 259static void blkverify_aio_bh(void *opaque)
 260{
 261    BlkverifyAIOCB *acb = opaque;
 262
 263    qemu_bh_delete(acb->bh);
 264    if (acb->buf) {
 265        qemu_iovec_destroy(&acb->raw_qiov);
 266        qemu_vfree(acb->buf);
 267    }
 268    acb->common.cb(acb->common.opaque, acb->ret);
 269    if (acb->finished) {
 270        *acb->finished = true;
 271    }
 272    qemu_aio_release(acb);
 273}
 274
 275static void blkverify_aio_cb(void *opaque, int ret)
 276{
 277    BlkverifyAIOCB *acb = opaque;
 278
 279    switch (++acb->done) {
 280    case 1:
 281        acb->ret = ret;
 282        break;
 283
 284    case 2:
 285        if (acb->ret != ret) {
 286            blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
 287        }
 288
 289        if (acb->verify) {
 290            acb->verify(acb);
 291        }
 292
 293        acb->bh = qemu_bh_new(blkverify_aio_bh, acb);
 294        qemu_bh_schedule(acb->bh);
 295        break;
 296    }
 297}
 298
 299static void blkverify_verify_readv(BlkverifyAIOCB *acb)
 300{
 301    ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
 302    if (offset != -1) {
 303        blkverify_err(acb, "contents mismatch in sector %" PRId64,
 304                      acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
 305    }
 306}
 307
 308static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
 309        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 310        BlockDriverCompletionFunc *cb, void *opaque)
 311{
 312    BDRVBlkverifyState *s = bs->opaque;
 313    BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
 314                                            nb_sectors, cb, opaque);
 315
 316    acb->verify = blkverify_verify_readv;
 317    acb->buf = qemu_blockalign(bs->file, qiov->size);
 318    qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
 319    blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
 320
 321    if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
 322                        blkverify_aio_cb, acb)) {
 323        blkverify_aio_cb(acb, -EIO);
 324    }
 325    if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
 326                        blkverify_aio_cb, acb)) {
 327        blkverify_aio_cb(acb, -EIO);
 328    }
 329    return &acb->common;
 330}
 331
 332static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
 333        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 334        BlockDriverCompletionFunc *cb, void *opaque)
 335{
 336    BDRVBlkverifyState *s = bs->opaque;
 337    BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
 338                                            nb_sectors, cb, opaque);
 339
 340    if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
 341                         blkverify_aio_cb, acb)) {
 342        blkverify_aio_cb(acb, -EIO);
 343    }
 344    if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
 345                         blkverify_aio_cb, acb)) {
 346        blkverify_aio_cb(acb, -EIO);
 347    }
 348    return &acb->common;
 349}
 350
 351static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
 352                                             BlockDriverCompletionFunc *cb,
 353                                             void *opaque)
 354{
 355    BDRVBlkverifyState *s = bs->opaque;
 356
 357    /* Only flush test file, the raw file is not important */
 358    return bdrv_aio_flush(s->test_file, cb, opaque);
 359}
 360
 361static BlockDriver bdrv_blkverify = {
 362    .format_name        = "blkverify",
 363    .protocol_name      = "blkverify",
 364
 365    .instance_size      = sizeof(BDRVBlkverifyState),
 366
 367    .bdrv_getlength     = blkverify_getlength,
 368
 369    .bdrv_file_open     = blkverify_open,
 370    .bdrv_close         = blkverify_close,
 371    .bdrv_flush         = blkverify_flush,
 372
 373    .bdrv_aio_readv     = blkverify_aio_readv,
 374    .bdrv_aio_writev    = blkverify_aio_writev,
 375    .bdrv_aio_flush     = blkverify_aio_flush,
 376};
 377
 378static void bdrv_blkverify_init(void)
 379{
 380    bdrv_register(&bdrv_blkverify);
 381}
 382
 383block_init(bdrv_blkverify_init);
 384