blkdebug.c 26.0 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
#include "qemu/option.h"
33 34
#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

630 631 632 633 634 635 636
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
                                                 bool want_zero,
                                                 int64_t offset,
                                                 int64_t bytes,
                                                 int64_t *pnum,
                                                 int64_t *map,
                                                 BlockDriverState **file)
E
Eric Blake 已提交
637
{
638 639 640
    assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
    return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
                                          pnum, map, file);
E
Eric Blake 已提交
641 642
}

K
Kevin Wolf 已提交
643 644 645
static void blkdebug_close(BlockDriverState *bs)
{
    BDRVBlkdebugState *s = bs->opaque;
K
Kevin Wolf 已提交
646 647 648
    BlkdebugRule *rule, *next;
    int i;

649
    for (i = 0; i < BLKDBG__MAX; i++) {
K
Kevin Wolf 已提交
650
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
K
Kevin Wolf 已提交
651
            remove_rule(rule);
K
Kevin Wolf 已提交
652 653
        }
    }
654 655

    g_free(s->config_file);
K
Kevin Wolf 已提交
656 657
}

658 659 660 661 662 663 664 665 666 667 668 669 670
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);

671 672 673
    if (!qtest_enabled()) {
        printf("blkdebug: Suspended request '%s'\n", r.tag);
    }
674
    qemu_coroutine_yield();
675 676 677
    if (!qtest_enabled()) {
        printf("blkdebug: Resuming request '%s'\n", r.tag);
    }
678 679 680 681 682

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

683
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
684
    bool injected)
K
Kevin Wolf 已提交
685 686 687 688
{
    BDRVBlkdebugState *s = bs->opaque;

    /* Only process rules for the current state */
689
    if (rule->state && rule->state != s->state) {
690
        return injected;
K
Kevin Wolf 已提交
691 692 693 694 695
    }

    /* Take the action */
    switch (rule->action) {
    case ACTION_INJECT_ERROR:
696 697 698 699 700
        if (!injected) {
            QSIMPLEQ_INIT(&s->active_rules);
            injected = true;
        }
        QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
K
Kevin Wolf 已提交
701 702 703
        break;

    case ACTION_SET_STATE:
704
        s->new_state = rule->options.set_state.new_state;
K
Kevin Wolf 已提交
705
        break;
706 707 708 709

    case ACTION_SUSPEND:
        suspend_request(bs, rule);
        break;
K
Kevin Wolf 已提交
710
    }
711
    return injected;
K
Kevin Wolf 已提交
712 713
}

714
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
K
Kevin Wolf 已提交
715 716
{
    BDRVBlkdebugState *s = bs->opaque;
717
    struct BlkdebugRule *rule, *next;
718
    bool injected;
K
Kevin Wolf 已提交
719

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

722
    injected = false;
723
    s->new_state = s->state;
724
    QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
725
        injected = process_rule(bs, rule, injected);
K
Kevin Wolf 已提交
726
    }
727
    s->state = s->new_state;
K
Kevin Wolf 已提交
728 729
}

730 731 732 733 734
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
                                     const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
    struct BlkdebugRule *rule;
735
    int blkdebug_event;
736

737
    blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL);
738
    if (blkdebug_event < 0) {
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
        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;
758
    BlkdebugSuspendedReq *r, *next;
759

760
    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
761
        if (!strcmp(r->tag, tag)) {
762
            qemu_coroutine_enter(r->co);
763 764 765 766 767 768
            return 0;
        }
    }
    return -ENOENT;
}

F
Fam Zheng 已提交
769 770 771 772
static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
                                            const char *tag)
{
    BDRVBlkdebugState *s = bs->opaque;
773
    BlkdebugSuspendedReq *r, *r_next;
F
Fam Zheng 已提交
774 775 776
    BlkdebugRule *rule, *next;
    int i, ret = -ENOENT;

777
    for (i = 0; i < BLKDBG__MAX; i++) {
F
Fam Zheng 已提交
778 779 780 781 782 783 784 785
        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;
            }
        }
    }
786
    QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
F
Fam Zheng 已提交
787
        if (!strcmp(r->tag, tag)) {
788
            qemu_coroutine_enter(r->co);
F
Fam Zheng 已提交
789 790 791 792 793
            ret = 0;
        }
    }
    return ret;
}
794 795 796 797 798 799 800 801 802 803 804 805 806 807

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;
}

808 809
static int64_t blkdebug_getlength(BlockDriverState *bs)
{
K
Kevin Wolf 已提交
810
    return bdrv_getlength(bs->file->bs);
811 812
}

813
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
814
{
815
    BDRVBlkdebugState *s = bs->opaque;
816
    QDict *opts;
817 818 819
    const QDictEntry *e;
    bool force_json = false;

820
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
821
        if (strcmp(qdict_entry_key(e), "config") &&
822
            strcmp(qdict_entry_key(e), "x-image"))
823 824 825 826 827
        {
            force_json = true;
            break;
        }
    }
828

K
Kevin Wolf 已提交
829
    if (force_json && !bs->file->bs->full_open_options) {
830 831 832 833 834
        /* The config file cannot be recreated, so creating a plain filename
         * is impossible */
        return;
    }

K
Kevin Wolf 已提交
835
    if (!force_json && bs->file->bs->exact_filename[0]) {
836 837 838 839 840 841 842
        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;
        }
843 844
    }

845
    opts = qdict_new();
846
    qdict_put_str(opts, "driver", "blkdebug");
847

K
Kevin Wolf 已提交
848
    QINCREF(bs->file->bs->full_open_options);
E
Eric Blake 已提交
849
    qdict_put(opts, "image", bs->file->bs->full_open_options);
850

851 852
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
        if (strcmp(qdict_entry_key(e), "x-image")) {
853 854
            qobject_incref(qdict_entry_value(e));
            qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
855 856 857 858 859 860
        }
    }

    bs->full_open_options = opts;
}

861 862 863 864 865
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
{
    BDRVBlkdebugState *s = bs->opaque;

    if (s->align) {
866
        bs->bl.request_alignment = s->align;
867
    }
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
    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;
    }
883 884
}

K
Kevin Wolf 已提交
885 886 887 888 889 890
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
                                   BlockReopenQueue *queue, Error **errp)
{
    return 0;
}

K
Kevin Wolf 已提交
891
static BlockDriver bdrv_blkdebug = {
892 893 894
    .format_name            = "blkdebug",
    .protocol_name          = "blkdebug",
    .instance_size          = sizeof(BDRVBlkdebugState),
895
    .is_filter              = true,
K
Kevin Wolf 已提交
896

897 898 899
    .bdrv_parse_filename    = blkdebug_parse_filename,
    .bdrv_file_open         = blkdebug_open,
    .bdrv_close             = blkdebug_close,
K
Kevin Wolf 已提交
900
    .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
901 902
    .bdrv_child_perm        = bdrv_filter_default_perms,

903
    .bdrv_getlength         = blkdebug_getlength,
904
    .bdrv_refresh_filename  = blkdebug_refresh_filename,
905
    .bdrv_refresh_limits    = blkdebug_refresh_limits,
K
Kevin Wolf 已提交
906

907 908 909
    .bdrv_co_preadv         = blkdebug_co_preadv,
    .bdrv_co_pwritev        = blkdebug_co_pwritev,
    .bdrv_co_flush_to_disk  = blkdebug_co_flush,
910 911
    .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes,
    .bdrv_co_pdiscard       = blkdebug_co_pdiscard,
912
    .bdrv_co_block_status   = blkdebug_co_block_status,
K
Kevin Wolf 已提交
913

914 915
    .bdrv_debug_event           = blkdebug_debug_event,
    .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
F
Fam Zheng 已提交
916 917
    .bdrv_debug_remove_breakpoint
                                = blkdebug_debug_remove_breakpoint,
918 919
    .bdrv_debug_resume          = blkdebug_debug_resume,
    .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
K
Kevin Wolf 已提交
920 921 922 923 924 925 926 927
};

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

block_init(bdrv_blkdebug_init);