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