blkdebug.c 25.3 KB
Newer Older
K
Kevin Wolf 已提交
1 2 3
/*
 * Block protocol for I/O error injection
 *
4
 * Copyright (C) 2016-2017 Red Hat, Inc.
K
Kevin Wolf 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

P
Peter Maydell 已提交
26
#include "qemu/osdep.h"
27
#include "qapi/error.h"
28
#include "qemu/cutils.h"
29
#include "qemu/config-file.h"
30
#include "block/block_int.h"
31
#include "qemu/module.h"
32 33 34
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
35
#include "sysemu/qtest.h"
K
Kevin Wolf 已提交
36 37

typedef struct BDRVBlkdebugState {
38
    int state;
39
    int new_state;
E
Eric Blake 已提交
40
    uint64_t align;
41 42 43 44 45
    uint64_t max_transfer;
    uint64_t opt_write_zero;
    uint64_t max_write_zero;
    uint64_t opt_discard;
    uint64_t max_discard;
46

47 48 49
    /* For blkdebug_refresh_filename() */
    char *config_file;

50
    QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
51
    QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
52
    QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
K
Kevin Wolf 已提交
53 54
} BDRVBlkdebugState;

K
Kevin Wolf 已提交
55
typedef struct BlkdebugAIOCB {
56
    BlockAIOCB common;
K
Kevin Wolf 已提交
57 58 59
    int ret;
} BlkdebugAIOCB;

60 61 62 63 64 65
typedef struct BlkdebugSuspendedReq {
    Coroutine *co;
    char *tag;
    QLIST_ENTRY(BlkdebugSuspendedReq) next;
} BlkdebugSuspendedReq;

K
Kevin Wolf 已提交
66 67 68
enum {
    ACTION_INJECT_ERROR,
    ACTION_SET_STATE,
69
    ACTION_SUSPEND,
K
Kevin Wolf 已提交
70 71 72
};

typedef struct BlkdebugRule {
73
    BlkdebugEvent event;
K
Kevin Wolf 已提交
74 75 76 77 78 79 80
    int action;
    int state;
    union {
        struct {
            int error;
            int immediately;
            int once;
81
            int64_t offset;
K
Kevin Wolf 已提交
82 83 84 85
        } inject;
        struct {
            int new_state;
        } set_state;
86 87 88
        struct {
            char *tag;
        } suspend;
K
Kevin Wolf 已提交
89 90
    } options;
    QLIST_ENTRY(BlkdebugRule) next;
91
    QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
K
Kevin Wolf 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
} BlkdebugRule;

static QemuOptsList inject_error_opts = {
    .name = "inject-error",
    .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
    .desc = {
        {
            .name = "event",
            .type = QEMU_OPT_STRING,
        },
        {
            .name = "state",
            .type = QEMU_OPT_NUMBER,
        },
        {
            .name = "errno",
            .type = QEMU_OPT_NUMBER,
        },
110 111 112 113
        {
            .name = "sector",
            .type = QEMU_OPT_NUMBER,
        },
K
Kevin Wolf 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126 127
        {
            .name = "once",
            .type = QEMU_OPT_BOOL,
        },
        {
            .name = "immediately",
            .type = QEMU_OPT_BOOL,
        },
        { /* end of list */ }
    },
};

static QemuOptsList set_state_opts = {
    .name = "set-state",
128
    .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
K
Kevin Wolf 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    .desc = {
        {
            .name = "event",
            .type = QEMU_OPT_STRING,
        },
        {
            .name = "state",
            .type = QEMU_OPT_NUMBER,
        },
        {
            .name = "new_state",
            .type = QEMU_OPT_NUMBER,
        },
        { /* end of list */ }
    },
};

static QemuOptsList *config_groups[] = {
    &inject_error_opts,
    &set_state_opts,
    NULL
};

struct add_rule_data {
    BDRVBlkdebugState *s;
    int action;
};

157
static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
K
Kevin Wolf 已提交
158 159 160 161
{
    struct add_rule_data *d = opaque;
    BDRVBlkdebugState *s = d->s;
    const char* event_name;
162
    int event;
K
Kevin Wolf 已提交
163
    struct BlkdebugRule *rule;
164
    int64_t sector;
K
Kevin Wolf 已提交
165 166 167

    /* Find the right event for the rule */
    event_name = qemu_opt_get(opts, "event");
168
    if (!event_name) {
169
        error_setg(errp, "Missing event name for rule");
170
        return -1;
171
    }
172
    event = qapi_enum_parse(&BlkdebugEvent_lookup, event_name, -1, errp);
173
    if (event < 0) {
K
Kevin Wolf 已提交
174 175 176 177
        return -1;
    }

    /* Set attributes common for all actions */
178
    rule = g_malloc0(sizeof(*rule));
K
Kevin Wolf 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191
    *rule = (struct BlkdebugRule) {
        .event  = event,
        .action = d->action,
        .state  = qemu_opt_get_number(opts, "state", 0),
    };

    /* Parse action-specific options */
    switch (d->action) {
    case ACTION_INJECT_ERROR:
        rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
        rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
        rule->options.inject.immediately =
            qemu_opt_get_bool(opts, "immediately", 0);
192 193 194
        sector = qemu_opt_get_number(opts, "sector", -1);
        rule->options.inject.offset =
            sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
K
Kevin Wolf 已提交
195 196 197 198 199 200
        break;

    case ACTION_SET_STATE:
        rule->options.set_state.new_state =
            qemu_opt_get_number(opts, "new_state", 0);
        break;
201 202 203 204 205

    case ACTION_SUSPEND:
        rule->options.suspend.tag =
            g_strdup(qemu_opt_get(opts, "tag"));
        break;
K
Kevin Wolf 已提交
206 207 208 209 210 211 212 213
    };

    /* Add the rule */
    QLIST_INSERT_HEAD(&s->rules[event], rule, next);

    return 0;
}

K
Kevin Wolf 已提交
214 215 216 217 218 219
static void remove_rule(BlkdebugRule *rule)
{
    switch (rule->action) {
    case ACTION_INJECT_ERROR:
    case ACTION_SET_STATE:
        break;
220 221 222
    case ACTION_SUSPEND:
        g_free(rule->options.suspend.tag);
        break;
K
Kevin Wolf 已提交
223 224 225 226 227 228
    }

    QLIST_REMOVE(rule, next);
    g_free(rule);
}

229 230
static int read_config(BDRVBlkdebugState *s, const char *filename,
                       QDict *options, Error **errp)
K
Kevin Wolf 已提交
231
{
M
Max Reitz 已提交
232
    FILE *f = NULL;
K
Kevin Wolf 已提交
233 234
    int ret;
    struct add_rule_data d;
235
    Error *local_err = NULL;
K
Kevin Wolf 已提交
236

M
Max Reitz 已提交
237 238 239 240 241 242
    if (filename) {
        f = fopen(filename, "r");
        if (f == NULL) {
            error_setg_errno(errp, errno, "Could not read blkdebug config file");
            return -errno;
        }
K
Kevin Wolf 已提交
243

M
Max Reitz 已提交
244 245 246 247 248
        ret = qemu_config_parse(f, config_groups, filename);
        if (ret < 0) {
            error_setg(errp, "Could not parse blkdebug config file");
            goto fail;
        }
K
Kevin Wolf 已提交
249 250
    }

251
    qemu_config_parse_qdict(options, config_groups, &local_err);
252
    if (local_err) {
253 254 255 256 257
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto fail;
    }

K
Kevin Wolf 已提交
258 259
    d.s = s;
    d.action = ACTION_INJECT_ERROR;
260
    qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
261 262 263 264 265
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto fail;
    }
K
Kevin Wolf 已提交
266 267

    d.action = ACTION_SET_STATE;
268
    qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
269 270 271 272 273
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto fail;
    }
K
Kevin Wolf 已提交
274 275 276

    ret = 0;
fail:
277 278
    qemu_opts_reset(&inject_error_opts);
    qemu_opts_reset(&set_state_opts);
M
Max Reitz 已提交
279 280 281
    if (f) {
        fclose(f);
    }
K
Kevin Wolf 已提交
282 283 284 285
    return ret;
}

/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
286 287
static void blkdebug_parse_filename(const char *filename, QDict *options,
                                    Error **errp)
K
Kevin Wolf 已提交
288
{
289
    const char *c;
K
Kevin Wolf 已提交
290

K
Kevin Wolf 已提交
291
    /* Parse the blkdebug: prefix */
292
    if (!strstart(filename, "blkdebug:", &filename)) {
293 294
        /* There was no prefix; therefore, all options have to be already
           present in the QDict (except for the filename) */
295
        qdict_put_str(options, "x-image", filename);
296
        return;
K
Kevin Wolf 已提交
297 298
    }

299
    /* Parse config file path */
K
Kevin Wolf 已提交
300 301
    c = strchr(filename, ':');
    if (c == NULL) {
302 303
        error_setg(errp, "blkdebug requires both config file and image path");
        return;
K
Kevin Wolf 已提交
304 305
    }

306 307 308 309
    if (c != filename) {
        QString *config_path;
        config_path = qstring_from_substr(filename, 0, c - filename - 1);
        qdict_put(options, "config", config_path);
K
Kevin Wolf 已提交
310
    }
311 312

    /* TODO Allow multi-level nesting and set file.filename here */
K
Kevin Wolf 已提交
313
    filename = c + 1;
314
    qdict_put_str(options, "x-image", filename);
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
}

static QemuOptsList runtime_opts = {
    .name = "blkdebug",
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
    .desc = {
        {
            .name = "config",
            .type = QEMU_OPT_STRING,
            .help = "Path to the configuration file",
        },
        {
            .name = "x-image",
            .type = QEMU_OPT_STRING,
            .help = "[internal use only, will be removed]",
        },
331 332 333 334 335
        {
            .name = "align",
            .type = QEMU_OPT_SIZE,
            .help = "Required alignment in bytes",
        },
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
        {
            .name = "max-transfer",
            .type = QEMU_OPT_SIZE,
            .help = "Maximum transfer size in bytes",
        },
        {
            .name = "opt-write-zero",
            .type = QEMU_OPT_SIZE,
            .help = "Optimum write zero alignment in bytes",
        },
        {
            .name = "max-write-zero",
            .type = QEMU_OPT_SIZE,
            .help = "Maximum write zero size in bytes",
        },
        {
            .name = "opt-discard",
            .type = QEMU_OPT_SIZE,
            .help = "Optimum discard alignment in bytes",
        },
        {
            .name = "max-discard",
            .type = QEMU_OPT_SIZE,
            .help = "Maximum discard size in bytes",
        },
361 362 363 364
        { /* end of list */ }
    },
};

M
Max Reitz 已提交
365 366
static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
                         Error **errp)
367 368 369 370 371
{
    BDRVBlkdebugState *s = bs->opaque;
    QemuOpts *opts;
    Error *local_err = NULL;
    int ret;
372
    uint64_t align;
373

374
    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
375
    qemu_opts_absorb_qdict(opts, options, &local_err);
376
    if (local_err) {
M
Max Reitz 已提交
377
        error_propagate(errp, local_err);
378
        ret = -EINVAL;
379
        goto out;
380 381
    }

382
    /* Read rules from config file or command line options */
383 384
    s->config_file = g_strdup(qemu_opt_get(opts, "config"));
    ret = read_config(s, s->config_file, options, errp);
M
Max Reitz 已提交
385
    if (ret) {
386
        goto out;
387
    }
K
Kevin Wolf 已提交
388

K
Kevin Wolf 已提交
389
    /* Set initial state */
390
    s->state = 1;
K
Kevin Wolf 已提交
391

392
    /* Open the image file */
K
Kevin Wolf 已提交
393 394 395 396
    bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
                               bs, &child_file, false, &local_err);
    if (local_err) {
        ret = -EINVAL;
M
Max Reitz 已提交
397
        error_propagate(errp, local_err);
398
        goto out;
K
Kevin Wolf 已提交
399 400
    }

401 402 403 404
    bs->supported_write_flags = BDRV_REQ_FUA &
        bs->file->bs->supported_write_flags;
    bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
        bs->file->bs->supported_zero_flags;
E
Eric Blake 已提交
405
    ret = -EINVAL;
406

407
    /* Set alignment overrides */
E
Eric Blake 已提交
408 409 410 411
    s->align = qemu_opt_get_size(opts, "align", 0);
    if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
        error_setg(errp, "Cannot meet constraints with align %" PRIu64,
                   s->align);
412
        goto out;
413
    }
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
    align = MAX(s->align, bs->file->bs->bl.request_alignment);

    s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
    if (s->max_transfer &&
        (s->max_transfer >= INT_MAX ||
         !QEMU_IS_ALIGNED(s->max_transfer, align))) {
        error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
                   s->max_transfer);
        goto out;
    }

    s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
    if (s->opt_write_zero &&
        (s->opt_write_zero >= INT_MAX ||
         !QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
        error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
                   s->opt_write_zero);
        goto out;
    }

    s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
    if (s->max_write_zero &&
        (s->max_write_zero >= INT_MAX ||
         !QEMU_IS_ALIGNED(s->max_write_zero,
                          MAX(s->opt_write_zero, align)))) {
        error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
                   s->max_write_zero);
        goto out;
    }

    s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
    if (s->opt_discard &&
        (s->opt_discard >= INT_MAX ||
         !QEMU_IS_ALIGNED(s->opt_discard, align))) {
        error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
                   s->opt_discard);
        goto out;
    }

    s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
    if (s->max_discard &&
        (s->max_discard >= INT_MAX ||
         !QEMU_IS_ALIGNED(s->max_discard,
                          MAX(s->opt_discard, align)))) {
        error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
                   s->max_discard);
        goto out;
    }
462

463
    ret = 0;
464
out:
465 466 467
    if (ret < 0) {
        g_free(s->config_file);
    }
468 469
    qemu_opts_del(opts);
    return ret;
K
Kevin Wolf 已提交
470 471
}

E
Eric Blake 已提交
472
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
K
Kevin Wolf 已提交
473 474
{
    BDRVBlkdebugState *s = bs->opaque;
E
Eric Blake 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
    BlkdebugRule *rule = NULL;
    int error;
    bool immediately;

    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
        uint64_t inject_offset = rule->options.inject.offset;

        if (inject_offset == -1 ||
            (bytes && inject_offset >= offset &&
             inject_offset < offset + bytes))
        {
            break;
        }
    }

    if (!rule || !rule->options.inject.error) {
        return 0;
    }

    immediately = rule->options.inject.immediately;
    error = rule->options.inject.error;
K
Kevin Wolf 已提交
496

497
    if (rule->options.inject.once) {
J
John Snow 已提交
498 499
        QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
        remove_rule(rule);
K
Kevin Wolf 已提交
500 501
    }

502
    if (!immediately) {
503
        aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
504
        qemu_coroutine_yield();
K
Kevin Wolf 已提交
505 506
    }

507
    return -error;
K
Kevin Wolf 已提交
508 509
}

510 511 512
static int coroutine_fn
blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
                   QEMUIOVector *qiov, int flags)
K
Kevin Wolf 已提交
513
{
E
Eric Blake 已提交
514
    int err;
515

516 517 518 519 520 521 522
    /* Sanity check block layer guarantees */
    assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
    assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
    if (bs->bl.max_transfer) {
        assert(bytes <= bs->bl.max_transfer);
    }

E
Eric Blake 已提交
523 524 525
    err = rule_check(bs, offset, bytes);
    if (err) {
        return err;
K
Kevin Wolf 已提交
526 527
    }

528
    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
K
Kevin Wolf 已提交
529 530
}

531 532 533
static int coroutine_fn
blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
                    QEMUIOVector *qiov, int flags)
K
Kevin Wolf 已提交
534
{
E
Eric Blake 已提交
535
    int err;
536

537 538 539 540 541 542 543
    /* Sanity check block layer guarantees */
    assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
    assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
    if (bs->bl.max_transfer) {
        assert(bytes <= bs->bl.max_transfer);
    }

E
Eric Blake 已提交
544 545 546
    err = rule_check(bs, offset, bytes);
    if (err) {
        return err;
K
Kevin Wolf 已提交
547 548
    }

549
    return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
K
Kevin Wolf 已提交
550 551
}

552
static int blkdebug_co_flush(BlockDriverState *bs)
553
{
E
Eric Blake 已提交
554
    int err = rule_check(bs, 0, 0);
555

E
Eric Blake 已提交
556 557
    if (err) {
        return err;
558 559
    }

560
    return bdrv_co_flush(bs->file->bs);
561 562
}

563
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
564
                                                  int64_t offset, int bytes,
565 566 567 568 569 570 571 572 573 574
                                                  BdrvRequestFlags flags)
{
    uint32_t align = MAX(bs->bl.request_alignment,
                         bs->bl.pwrite_zeroes_alignment);
    int err;

    /* Only pass through requests that are larger than requested
     * preferred alignment (so that we test the fallback to writes on
     * unaligned portions), and check that the block layer never hands
     * us anything unaligned that crosses an alignment boundary.  */
575
    if (bytes < align) {
576
        assert(QEMU_IS_ALIGNED(offset, align) ||
577
               QEMU_IS_ALIGNED(offset + bytes, align) ||
578
               DIV_ROUND_UP(offset, align) ==
579
               DIV_ROUND_UP(offset + bytes, align));
580 581 582
        return -ENOTSUP;
    }
    assert(QEMU_IS_ALIGNED(offset, align));
583
    assert(QEMU_IS_ALIGNED(bytes, align));
584
    if (bs->bl.max_pwrite_zeroes) {
585
        assert(bytes <= bs->bl.max_pwrite_zeroes);
586 587
    }

588
    err = rule_check(bs, offset, bytes);
589 590 591 592
    if (err) {
        return err;
    }

593
    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
594 595 596
}

static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
597
                                             int64_t offset, int bytes)
598 599 600 601 602 603 604
{
    uint32_t align = bs->bl.pdiscard_alignment;
    int err;

    /* Only pass through requests that are larger than requested
     * minimum alignment, and ensure that unaligned requests do not
     * cross optimum discard boundaries. */
605
    if (bytes < bs->bl.request_alignment) {
606
        assert(QEMU_IS_ALIGNED(offset, align) ||
607
               QEMU_IS_ALIGNED(offset + bytes, align) ||
608
               DIV_ROUND_UP(offset, align) ==
609
               DIV_ROUND_UP(offset + bytes, align));
610 611 612
        return -ENOTSUP;
    }
    assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
613 614
    assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
    if (align && bytes >= align) {
615
        assert(QEMU_IS_ALIGNED(offset, align));
616
        assert(QEMU_IS_ALIGNED(bytes, align));
617 618
    }
    if (bs->bl.max_pdiscard) {
619
        assert(bytes <= bs->bl.max_pdiscard);
620 621
    }

622
    err = rule_check(bs, offset, bytes);
623 624 625 626
    if (err) {
        return err;
    }

627
    return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
628
}
629

K
Kevin Wolf 已提交
630 631 632
static void blkdebug_close(BlockDriverState *bs)
{
    BDRVBlkdebugState *s = bs->opaque;
K
Kevin Wolf 已提交
633 634 635
    BlkdebugRule *rule, *next;
    int i;

636
    for (i = 0; i < BLKDBG__MAX; i++) {
K
Kevin Wolf 已提交
637
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
K
Kevin Wolf 已提交
638
            remove_rule(rule);
K
Kevin Wolf 已提交
639 640
        }
    }
641 642

    g_free(s->config_file);
K
Kevin Wolf 已提交
643 644
}

645 646 647 648 649 650 651 652 653 654 655 656 657
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
{
    BDRVBlkdebugState *s = bs->opaque;
    BlkdebugSuspendedReq r;

    r = (BlkdebugSuspendedReq) {
        .co         = qemu_coroutine_self(),
        .tag        = g_strdup(rule->options.suspend.tag),
    };

    remove_rule(rule);
    QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);

658 659 660
    if (!qtest_enabled()) {
        printf("blkdebug: Suspended request '%s'\n", r.tag);
    }
661
    qemu_coroutine_yield();
662 663 664
    if (!qtest_enabled()) {
        printf("blkdebug: Resuming request '%s'\n", r.tag);
    }
665 666 667 668 669

    QLIST_REMOVE(&r, next);
    g_free(r.tag);
}

670
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
671
    bool injected)
K
Kevin Wolf 已提交
672 673 674 675
{
    BDRVBlkdebugState *s = bs->opaque;

    /* Only process rules for the current state */
676
    if (rule->state && rule->state != s->state) {
677
        return injected;
K
Kevin Wolf 已提交
678 679 680 681 682
    }

    /* Take the action */
    switch (rule->action) {
    case ACTION_INJECT_ERROR:
683 684 685 686 687
        if (!injected) {
            QSIMPLEQ_INIT(&s->active_rules);
            injected = true;
        }
        QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
K
Kevin Wolf 已提交
688 689 690
        break;

    case ACTION_SET_STATE:
691
        s->new_state = rule->options.set_state.new_state;
K
Kevin Wolf 已提交
692
        break;
693 694 695 696

    case ACTION_SUSPEND:
        suspend_request(bs, rule);
        break;
K
Kevin Wolf 已提交
697
    }
698
    return injected;
K
Kevin Wolf 已提交
699 700
}

701
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
K
Kevin Wolf 已提交
702 703
{
    BDRVBlkdebugState *s = bs->opaque;
704
    struct BlkdebugRule *rule, *next;
705
    bool injected;
K
Kevin Wolf 已提交
706

707
    assert((int)event >= 0 && event < BLKDBG__MAX);
K
Kevin Wolf 已提交
708

709
    injected = false;
710
    s->new_state = s->state;
711
    QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
712
        injected = process_rule(bs, rule, injected);
K
Kevin Wolf 已提交
713
    }
714
    s->state = s->new_state;
K
Kevin Wolf 已提交
715 716
}

717 718 719 720 721
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
                                     const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
    struct BlkdebugRule *rule;
722
    int blkdebug_event;
723

724
    blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL);
725
    if (blkdebug_event < 0) {
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
        return -ENOENT;
    }

    rule = g_malloc(sizeof(*rule));
    *rule = (struct BlkdebugRule) {
        .event  = blkdebug_event,
        .action = ACTION_SUSPEND,
        .state  = 0,
        .options.suspend.tag = g_strdup(tag),
    };

    QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);

    return 0;
}

static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
745
    BlkdebugSuspendedReq *r, *next;
746

747
    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
748
        if (!strcmp(r->tag, tag)) {
749
            qemu_coroutine_enter(r->co);
750 751 752 753 754 755
            return 0;
        }
    }
    return -ENOENT;
}

F
Fam Zheng 已提交
756 757 758 759
static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
                                            const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
760
    BlkdebugSuspendedReq *r, *r_next;
F
Fam Zheng 已提交
761 762 763
    BlkdebugRule *rule, *next;
    int i, ret = -ENOENT;

764
    for (i = 0; i < BLKDBG__MAX; i++) {
F
Fam Zheng 已提交
765 766 767 768 769 770 771 772
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
            if (rule->action == ACTION_SUSPEND &&
                !strcmp(rule->options.suspend.tag, tag)) {
                remove_rule(rule);
                ret = 0;
            }
        }
    }
773
    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
F
Fam Zheng 已提交
774
        if (!strcmp(r->tag, tag)) {
775
            qemu_coroutine_enter(r->co);
F
Fam Zheng 已提交
776 777 778 779 780
            ret = 0;
        }
    }
    return ret;
}
781 782 783 784 785 786 787 788 789 790 791 792 793 794

static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
    BlkdebugSuspendedReq *r;

    QLIST_FOREACH(r, &s->suspended_reqs, next) {
        if (!strcmp(r->tag, tag)) {
            return true;
        }
    }
    return false;
}

795 796
static int64_t blkdebug_getlength(BlockDriverState *bs)
{
K
Kevin Wolf 已提交
797
    return bdrv_getlength(bs->file->bs);
798 799
}

800
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
801
{
802
    BDRVBlkdebugState *s = bs->opaque;
803
    QDict *opts;
804 805 806
    const QDictEntry *e;
    bool force_json = false;

807
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
808
        if (strcmp(qdict_entry_key(e), "config") &&
809
            strcmp(qdict_entry_key(e), "x-image"))
810 811 812 813 814
        {
            force_json = true;
            break;
        }
    }
815

K
Kevin Wolf 已提交
816
    if (force_json && !bs->file->bs->full_open_options) {
817 818 819 820 821
        /* The config file cannot be recreated, so creating a plain filename
         * is impossible */
        return;
    }

K
Kevin Wolf 已提交
822
    if (!force_json && bs->file->bs->exact_filename[0]) {
823 824 825 826 827 828 829
        int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
                           "blkdebug:%s:%s", s->config_file ?: "",
                           bs->file->bs->exact_filename);
        if (ret >= sizeof(bs->exact_filename)) {
            /* An overflow makes the filename unusable, so do not report any */
            bs->exact_filename[0] = 0;
        }
830 831
    }

832
    opts = qdict_new();
833
    qdict_put_str(opts, "driver", "blkdebug");
834

K
Kevin Wolf 已提交
835
    QINCREF(bs->file->bs->full_open_options);
E
Eric Blake 已提交
836
    qdict_put(opts, "image", bs->file->bs->full_open_options);
837

838 839
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
        if (strcmp(qdict_entry_key(e), "x-image")) {
840 841
            qobject_incref(qdict_entry_value(e));
            qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
842 843 844 845 846 847
        }
    }

    bs->full_open_options = opts;
}

848 849 850 851 852
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
{
    BDRVBlkdebugState *s = bs->opaque;

    if (s->align) {
853
        bs->bl.request_alignment = s->align;
854
    }
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    if (s->max_transfer) {
        bs->bl.max_transfer = s->max_transfer;
    }
    if (s->opt_write_zero) {
        bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
    }
    if (s->max_write_zero) {
        bs->bl.max_pwrite_zeroes = s->max_write_zero;
    }
    if (s->opt_discard) {
        bs->bl.pdiscard_alignment = s->opt_discard;
    }
    if (s->max_discard) {
        bs->bl.max_pdiscard = s->max_discard;
    }
870 871
}

K
Kevin Wolf 已提交
872 873 874 875 876 877
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
                                   BlockReopenQueue *queue, Error **errp)
{
    return 0;
}

K
Kevin Wolf 已提交
878
static BlockDriver bdrv_blkdebug = {
879 880 881
    .format_name            = "blkdebug",
    .protocol_name          = "blkdebug",
    .instance_size          = sizeof(BDRVBlkdebugState),
882
    .is_filter              = true,
K
Kevin Wolf 已提交
883

884 885 886
    .bdrv_parse_filename    = blkdebug_parse_filename,
    .bdrv_file_open         = blkdebug_open,
    .bdrv_close             = blkdebug_close,
K
Kevin Wolf 已提交
887
    .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
888 889
    .bdrv_child_perm        = bdrv_filter_default_perms,

890
    .bdrv_getlength         = blkdebug_getlength,
891
    .bdrv_refresh_filename  = blkdebug_refresh_filename,
892
    .bdrv_refresh_limits    = blkdebug_refresh_limits,
K
Kevin Wolf 已提交
893

894 895 896
    .bdrv_co_preadv         = blkdebug_co_preadv,
    .bdrv_co_pwritev        = blkdebug_co_pwritev,
    .bdrv_co_flush_to_disk  = blkdebug_co_flush,
897 898
    .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes,
    .bdrv_co_pdiscard       = blkdebug_co_pdiscard,
899
    .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file,
K
Kevin Wolf 已提交
900

901 902
    .bdrv_debug_event           = blkdebug_debug_event,
    .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
F
Fam Zheng 已提交
903 904
    .bdrv_debug_remove_breakpoint
                                = blkdebug_debug_remove_breakpoint,
905 906
    .bdrv_debug_resume          = blkdebug_debug_resume,
    .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
K
Kevin Wolf 已提交
907 908 909 910 911 912 913 914
};

static void bdrv_blkdebug_init(void)
{
    bdrv_register(&bdrv_blkdebug);
}

block_init(bdrv_blkdebug_init);