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 BlkverifyRequest {
  23    Coroutine *co;
  24    BlockDriverState *bs;
  25
  26    /* Request metadata */
  27    bool is_write;
  28    uint64_t offset;
  29    uint64_t bytes;
  30    int flags;
  31
  32    int (*request_fn)(BdrvChild *, int64_t, unsigned int, QEMUIOVector *,
  33                      BdrvRequestFlags);
  34
  35    int ret;                    /* test image result */
  36    int raw_ret;                /* raw image result */
  37
  38    unsigned int done;          /* completion counter */
  39
  40    QEMUIOVector *qiov;         /* user I/O vector */
  41    QEMUIOVector *raw_qiov;     /* cloned I/O vector for raw file */
  42} BlkverifyRequest;
  43
  44static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyRequest *r,
  45                                             const char *fmt, ...)
  46{
  47    va_list ap;
  48
  49    va_start(ap, fmt);
  50    fprintf(stderr, "blkverify: %s offset=%" PRId64 " bytes=%" PRId64 " ",
  51            r->is_write ? "write" : "read", r->offset, r->bytes);
  52    vfprintf(stderr, fmt, ap);
  53    fprintf(stderr, "\n");
  54    va_end(ap);
  55    exit(1);
  56}
  57
  58/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
  59static void blkverify_parse_filename(const char *filename, QDict *options,
  60                                     Error **errp)
  61{
  62    const char *c;
  63    QString *raw_path;
  64
  65
  66    /* Parse the blkverify: prefix */
  67    if (!strstart(filename, "blkverify:", &filename)) {
  68        /* There was no prefix; therefore, all options have to be already
  69           present in the QDict (except for the filename) */
  70        qdict_put_str(options, "x-image", filename);
  71        return;
  72    }
  73
  74    /* Parse the raw image filename */
  75    c = strchr(filename, ':');
  76    if (c == NULL) {
  77        error_setg(errp, "blkverify requires raw copy and original image path");
  78        return;
  79    }
  80
  81    /* TODO Implement option pass-through and set raw.filename here */
  82    raw_path = qstring_from_substr(filename, 0, c - filename - 1);
  83    qdict_put(options, "x-raw", raw_path);
  84
  85    /* TODO Allow multi-level nesting and set file.filename here */
  86    filename = c + 1;
  87    qdict_put_str(options, "x-image", filename);
  88}
  89
  90static QemuOptsList runtime_opts = {
  91    .name = "blkverify",
  92    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
  93    .desc = {
  94        {
  95            .name = "x-raw",
  96            .type = QEMU_OPT_STRING,
  97            .help = "[internal use only, will be removed]",
  98        },
  99        {
 100            .name = "x-image",
 101            .type = QEMU_OPT_STRING,
 102            .help = "[internal use only, will be removed]",
 103        },
 104        { /* end of list */ }
 105    },
 106};
 107
 108static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
 109                          Error **errp)
 110{
 111    BDRVBlkverifyState *s = bs->opaque;
 112    QemuOpts *opts;
 113    Error *local_err = NULL;
 114    int ret;
 115
 116    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
 117    qemu_opts_absorb_qdict(opts, options, &local_err);
 118    if (local_err) {
 119        error_propagate(errp, local_err);
 120        ret = -EINVAL;
 121        goto fail;
 122    }
 123
 124    /* Open the raw file */
 125    bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
 126                               bs, &child_file, false, &local_err);
 127    if (local_err) {
 128        ret = -EINVAL;
 129        error_propagate(errp, local_err);
 130        goto fail;
 131    }
 132
 133    /* Open the test file */
 134    s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options,
 135                                   "test", bs, &child_format, false,
 136                                   &local_err);
 137    if (local_err) {
 138        ret = -EINVAL;
 139        error_propagate(errp, local_err);
 140        goto fail;
 141    }
 142
 143    ret = 0;
 144fail:
 145    qemu_opts_del(opts);
 146    return ret;
 147}
 148
 149static void blkverify_close(BlockDriverState *bs)
 150{
 151    BDRVBlkverifyState *s = bs->opaque;
 152
 153    bdrv_unref_child(bs, s->test_file);
 154    s->test_file = NULL;
 155}
 156
 157static int64_t blkverify_getlength(BlockDriverState *bs)
 158{
 159    BDRVBlkverifyState *s = bs->opaque;
 160
 161    return bdrv_getlength(s->test_file->bs);
 162}
 163
 164static void coroutine_fn blkverify_do_test_req(void *opaque)
 165{
 166    BlkverifyRequest *r = opaque;
 167    BDRVBlkverifyState *s = r->bs->opaque;
 168
 169    r->ret = r->request_fn(s->test_file, r->offset, r->bytes, r->qiov,
 170                           r->flags);
 171    r->done++;
 172    qemu_coroutine_enter_if_inactive(r->co);
 173}
 174
 175static void coroutine_fn blkverify_do_raw_req(void *opaque)
 176{
 177    BlkverifyRequest *r = opaque;
 178
 179    r->raw_ret = r->request_fn(r->bs->file, r->offset, r->bytes, r->raw_qiov,
 180                               r->flags);
 181    r->done++;
 182    qemu_coroutine_enter_if_inactive(r->co);
 183}
 184
 185static int coroutine_fn
 186blkverify_co_prwv(BlockDriverState *bs, BlkverifyRequest *r, uint64_t offset,
 187                  uint64_t bytes, QEMUIOVector *qiov, QEMUIOVector *raw_qiov,
 188                  int flags, bool is_write)
 189{
 190    Coroutine *co_a, *co_b;
 191
 192    *r = (BlkverifyRequest) {
 193        .co         = qemu_coroutine_self(),
 194        .bs         = bs,
 195        .offset     = offset,
 196        .bytes      = bytes,
 197        .qiov       = qiov,
 198        .raw_qiov   = raw_qiov,
 199        .flags      = flags,
 200        .is_write   = is_write,
 201        .request_fn = is_write ? bdrv_co_pwritev : bdrv_co_preadv,
 202    };
 203
 204    co_a = qemu_coroutine_create(blkverify_do_test_req, r);
 205    co_b = qemu_coroutine_create(blkverify_do_raw_req, r);
 206
 207    qemu_coroutine_enter(co_a);
 208    qemu_coroutine_enter(co_b);
 209
 210    while (r->done < 2) {
 211        qemu_coroutine_yield();
 212    }
 213
 214    if (r->ret != r->raw_ret) {
 215        blkverify_err(r, "return value mismatch %d != %d", r->ret, r->raw_ret);
 216    }
 217
 218    return r->ret;
 219}
 220
 221static int coroutine_fn
 222blkverify_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 223                    QEMUIOVector *qiov, int flags)
 224{
 225    BlkverifyRequest r;
 226    QEMUIOVector raw_qiov;
 227    void *buf;
 228    ssize_t cmp_offset;
 229    int ret;
 230
 231    buf = qemu_blockalign(bs->file->bs, qiov->size);
 232    qemu_iovec_init(&raw_qiov, qiov->niov);
 233    qemu_iovec_clone(&raw_qiov, qiov, buf);
 234
 235    ret = blkverify_co_prwv(bs, &r, offset, bytes, qiov, &raw_qiov, flags,
 236                            false);
 237
 238    cmp_offset = qemu_iovec_compare(qiov, &raw_qiov);
 239    if (cmp_offset != -1) {
 240        blkverify_err(&r, "contents mismatch at offset %" PRId64,
 241                      offset + cmp_offset);
 242    }
 243
 244    qemu_iovec_destroy(&raw_qiov);
 245    qemu_vfree(buf);
 246
 247    return ret;
 248}
 249
 250static int coroutine_fn
 251blkverify_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
 252                     QEMUIOVector *qiov, int flags)
 253{
 254    BlkverifyRequest r;
 255    return blkverify_co_prwv(bs, &r, offset, bytes, qiov, qiov, flags, true);
 256}
 257
 258static int blkverify_co_flush(BlockDriverState *bs)
 259{
 260    BDRVBlkverifyState *s = bs->opaque;
 261
 262    /* Only flush test file, the raw file is not important */
 263    return bdrv_co_flush(s->test_file->bs);
 264}
 265
 266static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
 267                                                  BlockDriverState *candidate)
 268{
 269    BDRVBlkverifyState *s = bs->opaque;
 270
 271    bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
 272
 273    if (perm) {
 274        return true;
 275    }
 276
 277    return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
 278}
 279
 280static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
 281{
 282    BDRVBlkverifyState *s = bs->opaque;
 283
 284    /* bs->file->bs has already been refreshed */
 285    bdrv_refresh_filename(s->test_file->bs);
 286
 287    if (bs->file->bs->full_open_options
 288        && s->test_file->bs->full_open_options)
 289    {
 290        QDict *opts = qdict_new();
 291        qdict_put_str(opts, "driver", "blkverify");
 292
 293        QINCREF(bs->file->bs->full_open_options);
 294        qdict_put(opts, "raw", bs->file->bs->full_open_options);
 295        QINCREF(s->test_file->bs->full_open_options);
 296        qdict_put(opts, "test", s->test_file->bs->full_open_options);
 297
 298        bs->full_open_options = opts;
 299    }
 300
 301    if (bs->file->bs->exact_filename[0]
 302        && s->test_file->bs->exact_filename[0])
 303    {
 304        int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 305                           "blkverify:%s:%s",
 306                           bs->file->bs->exact_filename,
 307                           s->test_file->bs->exact_filename);
 308        if (ret >= sizeof(bs->exact_filename)) {
 309            /* An overflow makes the filename unusable, so do not report any */
 310            bs->exact_filename[0] = 0;
 311        }
 312    }
 313}
 314
 315static BlockDriver bdrv_blkverify = {
 316    .format_name                      = "blkverify",
 317    .protocol_name                    = "blkverify",
 318    .instance_size                    = sizeof(BDRVBlkverifyState),
 319
 320    .bdrv_parse_filename              = blkverify_parse_filename,
 321    .bdrv_file_open                   = blkverify_open,
 322    .bdrv_close                       = blkverify_close,
 323    .bdrv_child_perm                  = bdrv_filter_default_perms,
 324    .bdrv_getlength                   = blkverify_getlength,
 325    .bdrv_refresh_filename            = blkverify_refresh_filename,
 326
 327    .bdrv_co_preadv                   = blkverify_co_preadv,
 328    .bdrv_co_pwritev                  = blkverify_co_pwritev,
 329    .bdrv_co_flush                    = blkverify_co_flush,
 330
 331    .is_filter                        = true,
 332    .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
 333};
 334
 335static void bdrv_blkverify_init(void)
 336{
 337    bdrv_register(&bdrv_blkverify);
 338}
 339
 340block_init(bdrv_blkverify_init);
 341