blkdebug.c 25.6 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 249
        ret = qemu_config_parse(f, config_groups, filename);
        if (ret < 0) {
            error_setg(errp, "Could not parse blkdebug config file");
            ret = -EINVAL;
            goto fail;
        }
K
Kevin Wolf 已提交
250 251
    }

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

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

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

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

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

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

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

307 308 309 310
    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 已提交
311
    }
312 313

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

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]",
        },
332 333 334 335 336
        {
            .name = "align",
            .type = QEMU_OPT_SIZE,
            .help = "Required alignment in bytes",
        },
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
        {
            .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",
        },
362 363 364 365
        { /* end of list */ }
    },
};

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

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

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

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

393
    /* Open the image file */
K
Kevin Wolf 已提交
394 395 396 397
    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 已提交
398
        error_propagate(errp, local_err);
399
        goto out;
K
Kevin Wolf 已提交
400 401
    }

402 403 404 405
    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 已提交
406
    ret = -EINVAL;
407

408
    /* Set alignment overrides */
E
Eric Blake 已提交
409 410 411 412
    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);
413
        goto out;
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 462
    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;
    }
463

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

E
Eric Blake 已提交
473
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
K
Kevin Wolf 已提交
474 475
{
    BDRVBlkdebugState *s = bs->opaque;
E
Eric Blake 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    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 已提交
497

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

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

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

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

517 518 519 520 521 522 523
    /* 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 已提交
524 525 526
    err = rule_check(bs, offset, bytes);
    if (err) {
        return err;
K
Kevin Wolf 已提交
527 528
    }

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

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

538 539 540 541 542 543 544
    /* 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 已提交
545 546 547
    err = rule_check(bs, offset, bytes);
    if (err) {
        return err;
K
Kevin Wolf 已提交
548 549
    }

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

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

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

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

564
static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
565
                                                  int64_t offset, int bytes,
566 567 568 569 570 571 572 573 574 575
                                                  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.  */
576
    if (bytes < align) {
577
        assert(QEMU_IS_ALIGNED(offset, align) ||
578
               QEMU_IS_ALIGNED(offset + bytes, align) ||
579
               DIV_ROUND_UP(offset, align) ==
580
               DIV_ROUND_UP(offset + bytes, align));
581 582 583
        return -ENOTSUP;
    }
    assert(QEMU_IS_ALIGNED(offset, align));
584
    assert(QEMU_IS_ALIGNED(bytes, align));
585
    if (bs->bl.max_pwrite_zeroes) {
586
        assert(bytes <= bs->bl.max_pwrite_zeroes);
587 588
    }

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

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

static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
598
                                             int64_t offset, int bytes)
599 600 601 602 603 604 605
{
    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. */
606
    if (bytes < bs->bl.request_alignment) {
607
        assert(QEMU_IS_ALIGNED(offset, align) ||
608
               QEMU_IS_ALIGNED(offset + bytes, align) ||
609
               DIV_ROUND_UP(offset, align) ==
610
               DIV_ROUND_UP(offset + bytes, align));
611 612 613
        return -ENOTSUP;
    }
    assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
614 615
    assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
    if (align && bytes >= align) {
616
        assert(QEMU_IS_ALIGNED(offset, align));
617
        assert(QEMU_IS_ALIGNED(bytes, align));
618 619
    }
    if (bs->bl.max_pdiscard) {
620
        assert(bytes <= bs->bl.max_pdiscard);
621 622
    }

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

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

631 632 633 634 635 636 637 638 639 640
static int64_t coroutine_fn blkdebug_co_get_block_status(
    BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
    BlockDriverState **file)
{
    *pnum = nb_sectors;
    *file = bs->file->bs;
    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
        (sector_num << BDRV_SECTOR_BITS);
}

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

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

    g_free(s->config_file);
K
Kevin Wolf 已提交
654 655
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

843
    opts = qdict_new();
844
    qdict_put_str(opts, "driver", "blkdebug");
845

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

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

    bs->full_open_options = opts;
}

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

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

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

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

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

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

905 906 907
    .bdrv_co_preadv         = blkdebug_co_preadv,
    .bdrv_co_pwritev        = blkdebug_co_pwritev,
    .bdrv_co_flush_to_disk  = blkdebug_co_flush,
908 909
    .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes,
    .bdrv_co_pdiscard       = blkdebug_co_pdiscard,
910
    .bdrv_co_get_block_status = blkdebug_co_get_block_status,
K
Kevin Wolf 已提交
911

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

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

block_init(bdrv_blkdebug_init);