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