qemu/block/blkdebug.c
<<
>>
Prefs
   1/*
   2 * Block protocol for I/O error injection
   3 *
   4 * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qapi/error.h"
  27#include "qemu/cutils.h"
  28#include "qemu/config-file.h"
  29#include "block/block_int.h"
  30#include "qemu/module.h"
  31#include "qapi/qmp/qbool.h"
  32#include "qapi/qmp/qdict.h"
  33#include "qapi/qmp/qint.h"
  34#include "qapi/qmp/qstring.h"
  35#include "sysemu/qtest.h"
  36
  37typedef struct BDRVBlkdebugState {
  38    int state;
  39    int new_state;
  40    int align;
  41
  42    /* For blkdebug_refresh_filename() */
  43    char *config_file;
  44
  45    QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
  46    QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
  47    QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
  48} BDRVBlkdebugState;
  49
  50typedef struct BlkdebugAIOCB {
  51    BlockAIOCB common;
  52    int ret;
  53} BlkdebugAIOCB;
  54
  55typedef struct BlkdebugSuspendedReq {
  56    Coroutine *co;
  57    char *tag;
  58    QLIST_ENTRY(BlkdebugSuspendedReq) next;
  59} BlkdebugSuspendedReq;
  60
  61static const AIOCBInfo blkdebug_aiocb_info = {
  62    .aiocb_size    = sizeof(BlkdebugAIOCB),
  63};
  64
  65enum {
  66    ACTION_INJECT_ERROR,
  67    ACTION_SET_STATE,
  68    ACTION_SUSPEND,
  69};
  70
  71typedef struct BlkdebugRule {
  72    BlkdebugEvent event;
  73    int action;
  74    int state;
  75    union {
  76        struct {
  77            int error;
  78            int immediately;
  79            int once;
  80            int64_t sector;
  81        } inject;
  82        struct {
  83            int new_state;
  84        } set_state;
  85        struct {
  86            char *tag;
  87        } suspend;
  88    } options;
  89    QLIST_ENTRY(BlkdebugRule) next;
  90    QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
  91} BlkdebugRule;
  92
  93static QemuOptsList inject_error_opts = {
  94    .name = "inject-error",
  95    .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
  96    .desc = {
  97        {
  98            .name = "event",
  99            .type = QEMU_OPT_STRING,
 100        },
 101        {
 102            .name = "state",
 103            .type = QEMU_OPT_NUMBER,
 104        },
 105        {
 106            .name = "errno",
 107            .type = QEMU_OPT_NUMBER,
 108        },
 109        {
 110            .name = "sector",
 111            .type = QEMU_OPT_NUMBER,
 112        },
 113        {
 114            .name = "once",
 115            .type = QEMU_OPT_BOOL,
 116        },
 117        {
 118            .name = "immediately",
 119            .type = QEMU_OPT_BOOL,
 120        },
 121        { /* end of list */ }
 122    },
 123};
 124
 125static QemuOptsList set_state_opts = {
 126    .name = "set-state",
 127    .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
 128    .desc = {
 129        {
 130            .name = "event",
 131            .type = QEMU_OPT_STRING,
 132        },
 133        {
 134            .name = "state",
 135            .type = QEMU_OPT_NUMBER,
 136        },
 137        {
 138            .name = "new_state",
 139            .type = QEMU_OPT_NUMBER,
 140        },
 141        { /* end of list */ }
 142    },
 143};
 144
 145static QemuOptsList *config_groups[] = {
 146    &inject_error_opts,
 147    &set_state_opts,
 148    NULL
 149};
 150
 151static int get_event_by_name(const char *name, BlkdebugEvent *event)
 152{
 153    int i;
 154
 155    for (i = 0; i < BLKDBG__MAX; i++) {
 156        if (!strcmp(BlkdebugEvent_lookup[i], name)) {
 157            *event = i;
 158            return 0;
 159        }
 160    }
 161
 162    return -1;
 163}
 164
 165struct add_rule_data {
 166    BDRVBlkdebugState *s;
 167    int action;
 168};
 169
 170static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
 171{
 172    struct add_rule_data *d = opaque;
 173    BDRVBlkdebugState *s = d->s;
 174    const char* event_name;
 175    BlkdebugEvent event;
 176    struct BlkdebugRule *rule;
 177
 178    /* Find the right event for the rule */
 179    event_name = qemu_opt_get(opts, "event");
 180    if (!event_name) {
 181        error_setg(errp, "Missing event name for rule");
 182        return -1;
 183    } else if (get_event_by_name(event_name, &event) < 0) {
 184        error_setg(errp, "Invalid event name \"%s\"", event_name);
 185        return -1;
 186    }
 187
 188    /* Set attributes common for all actions */
 189    rule = g_malloc0(sizeof(*rule));
 190    *rule = (struct BlkdebugRule) {
 191        .event  = event,
 192        .action = d->action,
 193        .state  = qemu_opt_get_number(opts, "state", 0),
 194    };
 195
 196    /* Parse action-specific options */
 197    switch (d->action) {
 198    case ACTION_INJECT_ERROR:
 199        rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
 200        rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
 201        rule->options.inject.immediately =
 202            qemu_opt_get_bool(opts, "immediately", 0);
 203        rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
 204        break;
 205
 206    case ACTION_SET_STATE:
 207        rule->options.set_state.new_state =
 208            qemu_opt_get_number(opts, "new_state", 0);
 209        break;
 210
 211    case ACTION_SUSPEND:
 212        rule->options.suspend.tag =
 213            g_strdup(qemu_opt_get(opts, "tag"));
 214        break;
 215    };
 216
 217    /* Add the rule */
 218    QLIST_INSERT_HEAD(&s->rules[event], rule, next);
 219
 220    return 0;
 221}
 222
 223static void remove_rule(BlkdebugRule *rule)
 224{
 225    switch (rule->action) {
 226    case ACTION_INJECT_ERROR:
 227    case ACTION_SET_STATE:
 228        break;
 229    case ACTION_SUSPEND:
 230        g_free(rule->options.suspend.tag);
 231        break;
 232    }
 233
 234    QLIST_REMOVE(rule, next);
 235    g_free(rule);
 236}
 237
 238static int read_config(BDRVBlkdebugState *s, const char *filename,
 239                       QDict *options, Error **errp)
 240{
 241    FILE *f = NULL;
 242    int ret;
 243    struct add_rule_data d;
 244    Error *local_err = NULL;
 245
 246    if (filename) {
 247        f = fopen(filename, "r");
 248        if (f == NULL) {
 249            error_setg_errno(errp, errno, "Could not read blkdebug config file");
 250            return -errno;
 251        }
 252
 253        ret = qemu_config_parse(f, config_groups, filename);
 254        if (ret < 0) {
 255            error_setg(errp, "Could not parse blkdebug config file");
 256            ret = -EINVAL;
 257            goto fail;
 258        }
 259    }
 260
 261    qemu_config_parse_qdict(options, config_groups, &local_err);
 262    if (local_err) {
 263        error_propagate(errp, local_err);
 264        ret = -EINVAL;
 265        goto fail;
 266    }
 267
 268    d.s = s;
 269    d.action = ACTION_INJECT_ERROR;
 270    qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
 271    if (local_err) {
 272        error_propagate(errp, local_err);
 273        ret = -EINVAL;
 274        goto fail;
 275    }
 276
 277    d.action = ACTION_SET_STATE;
 278    qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
 279    if (local_err) {
 280        error_propagate(errp, local_err);
 281        ret = -EINVAL;
 282        goto fail;
 283    }
 284
 285    ret = 0;
 286fail:
 287    qemu_opts_reset(&inject_error_opts);
 288    qemu_opts_reset(&set_state_opts);
 289    if (f) {
 290        fclose(f);
 291    }
 292    return ret;
 293}
 294
 295/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
 296static void blkdebug_parse_filename(const char *filename, QDict *options,
 297                                    Error **errp)
 298{
 299    const char *c;
 300
 301    /* Parse the blkdebug: prefix */
 302    if (!strstart(filename, "blkdebug:", &filename)) {
 303        /* There was no prefix; therefore, all options have to be already
 304           present in the QDict (except for the filename) */
 305        qdict_put(options, "x-image", qstring_from_str(filename));
 306        return;
 307    }
 308
 309    /* Parse config file path */
 310    c = strchr(filename, ':');
 311    if (c == NULL) {
 312        error_setg(errp, "blkdebug requires both config file and image path");
 313        return;
 314    }
 315
 316    if (c != filename) {
 317        QString *config_path;
 318        config_path = qstring_from_substr(filename, 0, c - filename - 1);
 319        qdict_put(options, "config", config_path);
 320    }
 321
 322    /* TODO Allow multi-level nesting and set file.filename here */
 323    filename = c + 1;
 324    qdict_put(options, "x-image", qstring_from_str(filename));
 325}
 326
 327static QemuOptsList runtime_opts = {
 328    .name = "blkdebug",
 329    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
 330    .desc = {
 331        {
 332            .name = "config",
 333            .type = QEMU_OPT_STRING,
 334            .help = "Path to the configuration file",
 335        },
 336        {
 337            .name = "x-image",
 338            .type = QEMU_OPT_STRING,
 339            .help = "[internal use only, will be removed]",
 340        },
 341        {
 342            .name = "align",
 343            .type = QEMU_OPT_SIZE,
 344            .help = "Required alignment in bytes",
 345        },
 346        { /* end of list */ }
 347    },
 348};
 349
 350static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
 351                         Error **errp)
 352{
 353    BDRVBlkdebugState *s = bs->opaque;
 354    QemuOpts *opts;
 355    Error *local_err = NULL;
 356    uint64_t align;
 357    int ret;
 358
 359    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
 360    qemu_opts_absorb_qdict(opts, options, &local_err);
 361    if (local_err) {
 362        error_propagate(errp, local_err);
 363        ret = -EINVAL;
 364        goto out;
 365    }
 366
 367    /* Read rules from config file or command line options */
 368    s->config_file = g_strdup(qemu_opt_get(opts, "config"));
 369    ret = read_config(s, s->config_file, options, errp);
 370    if (ret) {
 371        goto out;
 372    }
 373
 374    /* Set initial state */
 375    s->state = 1;
 376
 377    /* Open the image file */
 378    bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
 379                               bs, &child_file, false, &local_err);
 380    if (local_err) {
 381        ret = -EINVAL;
 382        error_propagate(errp, local_err);
 383        goto out;
 384    }
 385
 386    /* Set request alignment */
 387    align = qemu_opt_get_size(opts, "align", 0);
 388    if (align < INT_MAX && is_power_of_2(align)) {
 389        s->align = align;
 390    } else if (align) {
 391        error_setg(errp, "Invalid alignment");
 392        ret = -EINVAL;
 393        goto fail_unref;
 394    }
 395
 396    ret = 0;
 397    goto out;
 398
 399fail_unref:
 400    bdrv_unref_child(bs, bs->file);
 401out:
 402    if (ret < 0) {
 403        g_free(s->config_file);
 404    }
 405    qemu_opts_del(opts);
 406    return ret;
 407}
 408
 409static void error_callback_bh(void *opaque)
 410{
 411    struct BlkdebugAIOCB *acb = opaque;
 412    acb->common.cb(acb->common.opaque, acb->ret);
 413    qemu_aio_unref(acb);
 414}
 415
 416static BlockAIOCB *inject_error(BlockDriverState *bs,
 417    BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
 418{
 419    BDRVBlkdebugState *s = bs->opaque;
 420    int error = rule->options.inject.error;
 421    struct BlkdebugAIOCB *acb;
 422    bool immediately = rule->options.inject.immediately;
 423
 424    if (rule->options.inject.once) {
 425        QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
 426        remove_rule(rule);
 427    }
 428
 429    if (immediately) {
 430        return NULL;
 431    }
 432
 433    acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
 434    acb->ret = -error;
 435
 436    aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh, acb);
 437
 438    return &acb->common;
 439}
 440
 441static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
 442    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 443    BlockCompletionFunc *cb, void *opaque)
 444{
 445    BDRVBlkdebugState *s = bs->opaque;
 446    BlkdebugRule *rule = NULL;
 447
 448    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
 449        if (rule->options.inject.sector == -1 ||
 450            (rule->options.inject.sector >= sector_num &&
 451             rule->options.inject.sector < sector_num + nb_sectors)) {
 452            break;
 453        }
 454    }
 455
 456    if (rule && rule->options.inject.error) {
 457        return inject_error(bs, cb, opaque, rule);
 458    }
 459
 460    return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors,
 461                          cb, opaque);
 462}
 463
 464static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
 465    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
 466    BlockCompletionFunc *cb, void *opaque)
 467{
 468    BDRVBlkdebugState *s = bs->opaque;
 469    BlkdebugRule *rule = NULL;
 470
 471    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
 472        if (rule->options.inject.sector == -1 ||
 473            (rule->options.inject.sector >= sector_num &&
 474             rule->options.inject.sector < sector_num + nb_sectors)) {
 475            break;
 476        }
 477    }
 478
 479    if (rule && rule->options.inject.error) {
 480        return inject_error(bs, cb, opaque, rule);
 481    }
 482
 483    return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
 484                           cb, opaque);
 485}
 486
 487static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
 488    BlockCompletionFunc *cb, void *opaque)
 489{
 490    BDRVBlkdebugState *s = bs->opaque;
 491    BlkdebugRule *rule = NULL;
 492
 493    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
 494        if (rule->options.inject.sector == -1) {
 495            break;
 496        }
 497    }
 498
 499    if (rule && rule->options.inject.error) {
 500        return inject_error(bs, cb, opaque, rule);
 501    }
 502
 503    return bdrv_aio_flush(bs->file->bs, cb, opaque);
 504}
 505
 506
 507static void blkdebug_close(BlockDriverState *bs)
 508{
 509    BDRVBlkdebugState *s = bs->opaque;
 510    BlkdebugRule *rule, *next;
 511    int i;
 512
 513    for (i = 0; i < BLKDBG__MAX; i++) {
 514        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
 515            remove_rule(rule);
 516        }
 517    }
 518
 519    g_free(s->config_file);
 520}
 521
 522static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
 523{
 524    BDRVBlkdebugState *s = bs->opaque;
 525    BlkdebugSuspendedReq r;
 526
 527    r = (BlkdebugSuspendedReq) {
 528        .co         = qemu_coroutine_self(),
 529        .tag        = g_strdup(rule->options.suspend.tag),
 530    };
 531
 532    remove_rule(rule);
 533    QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
 534
 535    if (!qtest_enabled()) {
 536        printf("blkdebug: Suspended request '%s'\n", r.tag);
 537    }
 538    qemu_coroutine_yield();
 539    if (!qtest_enabled()) {
 540        printf("blkdebug: Resuming request '%s'\n", r.tag);
 541    }
 542
 543    QLIST_REMOVE(&r, next);
 544    g_free(r.tag);
 545}
 546
 547static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
 548    bool injected)
 549{
 550    BDRVBlkdebugState *s = bs->opaque;
 551
 552    /* Only process rules for the current state */
 553    if (rule->state && rule->state != s->state) {
 554        return injected;
 555    }
 556
 557    /* Take the action */
 558    switch (rule->action) {
 559    case ACTION_INJECT_ERROR:
 560        if (!injected) {
 561            QSIMPLEQ_INIT(&s->active_rules);
 562            injected = true;
 563        }
 564        QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
 565        break;
 566
 567    case ACTION_SET_STATE:
 568        s->new_state = rule->options.set_state.new_state;
 569        break;
 570
 571    case ACTION_SUSPEND:
 572        suspend_request(bs, rule);
 573        break;
 574    }
 575    return injected;
 576}
 577
 578static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
 579{
 580    BDRVBlkdebugState *s = bs->opaque;
 581    struct BlkdebugRule *rule, *next;
 582    bool injected;
 583
 584    assert((int)event >= 0 && event < BLKDBG__MAX);
 585
 586    injected = false;
 587    s->new_state = s->state;
 588    QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
 589        injected = process_rule(bs, rule, injected);
 590    }
 591    s->state = s->new_state;
 592}
 593
 594static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
 595                                     const char *tag)
 596{
 597    BDRVBlkdebugState *s = bs->opaque;
 598    struct BlkdebugRule *rule;
 599    BlkdebugEvent blkdebug_event;
 600
 601    if (get_event_by_name(event, &blkdebug_event) < 0) {
 602        return -ENOENT;
 603    }
 604
 605
 606    rule = g_malloc(sizeof(*rule));
 607    *rule = (struct BlkdebugRule) {
 608        .event  = blkdebug_event,
 609        .action = ACTION_SUSPEND,
 610        .state  = 0,
 611        .options.suspend.tag = g_strdup(tag),
 612    };
 613
 614    QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
 615
 616    return 0;
 617}
 618
 619static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
 620{
 621    BDRVBlkdebugState *s = bs->opaque;
 622    BlkdebugSuspendedReq *r, *next;
 623
 624    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
 625        if (!strcmp(r->tag, tag)) {
 626            qemu_coroutine_enter(r->co);
 627            return 0;
 628        }
 629    }
 630    return -ENOENT;
 631}
 632
 633static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
 634                                            const char *tag)
 635{
 636    BDRVBlkdebugState *s = bs->opaque;
 637    BlkdebugSuspendedReq *r, *r_next;
 638    BlkdebugRule *rule, *next;
 639    int i, ret = -ENOENT;
 640
 641    for (i = 0; i < BLKDBG__MAX; i++) {
 642        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
 643            if (rule->action == ACTION_SUSPEND &&
 644                !strcmp(rule->options.suspend.tag, tag)) {
 645                remove_rule(rule);
 646                ret = 0;
 647            }
 648        }
 649    }
 650    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
 651        if (!strcmp(r->tag, tag)) {
 652            qemu_coroutine_enter(r->co);
 653            ret = 0;
 654        }
 655    }
 656    return ret;
 657}
 658
 659static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
 660{
 661    BDRVBlkdebugState *s = bs->opaque;
 662    BlkdebugSuspendedReq *r;
 663
 664    QLIST_FOREACH(r, &s->suspended_reqs, next) {
 665        if (!strcmp(r->tag, tag)) {
 666            return true;
 667        }
 668    }
 669    return false;
 670}
 671
 672static int64_t blkdebug_getlength(BlockDriverState *bs)
 673{
 674    return bdrv_getlength(bs->file->bs);
 675}
 676
 677static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
 678{
 679    return bdrv_truncate(bs->file->bs, offset);
 680}
 681
 682static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
 683{
 684    BDRVBlkdebugState *s = bs->opaque;
 685    QDict *opts;
 686    const QDictEntry *e;
 687    bool force_json = false;
 688
 689    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
 690        if (strcmp(qdict_entry_key(e), "config") &&
 691            strcmp(qdict_entry_key(e), "x-image"))
 692        {
 693            force_json = true;
 694            break;
 695        }
 696    }
 697
 698    if (force_json && !bs->file->bs->full_open_options) {
 699        /* The config file cannot be recreated, so creating a plain filename
 700         * is impossible */
 701        return;
 702    }
 703
 704    if (!force_json && bs->file->bs->exact_filename[0]) {
 705        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 706                 "blkdebug:%s:%s", s->config_file ?: "",
 707                 bs->file->bs->exact_filename);
 708    }
 709
 710    opts = qdict_new();
 711    qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
 712
 713    QINCREF(bs->file->bs->full_open_options);
 714    qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
 715
 716    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
 717        if (strcmp(qdict_entry_key(e), "x-image")) {
 718            qobject_incref(qdict_entry_value(e));
 719            qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
 720        }
 721    }
 722
 723    bs->full_open_options = opts;
 724}
 725
 726static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
 727{
 728    BDRVBlkdebugState *s = bs->opaque;
 729
 730    if (s->align) {
 731        bs->bl.request_alignment = s->align;
 732    }
 733}
 734
 735static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
 736                                   BlockReopenQueue *queue, Error **errp)
 737{
 738    return 0;
 739}
 740
 741static BlockDriver bdrv_blkdebug = {
 742    .format_name            = "blkdebug",
 743    .protocol_name          = "blkdebug",
 744    .instance_size          = sizeof(BDRVBlkdebugState),
 745
 746    .bdrv_parse_filename    = blkdebug_parse_filename,
 747    .bdrv_file_open         = blkdebug_open,
 748    .bdrv_close             = blkdebug_close,
 749    .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
 750    .bdrv_getlength         = blkdebug_getlength,
 751    .bdrv_truncate          = blkdebug_truncate,
 752    .bdrv_refresh_filename  = blkdebug_refresh_filename,
 753    .bdrv_refresh_limits    = blkdebug_refresh_limits,
 754
 755    .bdrv_aio_readv         = blkdebug_aio_readv,
 756    .bdrv_aio_writev        = blkdebug_aio_writev,
 757    .bdrv_aio_flush         = blkdebug_aio_flush,
 758
 759    .bdrv_debug_event           = blkdebug_debug_event,
 760    .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
 761    .bdrv_debug_remove_breakpoint
 762                                = blkdebug_debug_remove_breakpoint,
 763    .bdrv_debug_resume          = blkdebug_debug_resume,
 764    .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
 765};
 766
 767static void bdrv_blkdebug_init(void)
 768{
 769    bdrv_register(&bdrv_blkdebug);
 770}
 771
 772block_init(bdrv_blkdebug_init);
 773