Statistics
| Branch: | Revision:

root / block / blkdebug.c @ 34b5d2c6

History | View | Annotate | Download (17.6 kB)

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

    
25
#include "qemu-common.h"
26
#include "qemu/config-file.h"
27
#include "block/block_int.h"
28
#include "qemu/module.h"
29

    
30
typedef struct BDRVBlkdebugState {
31
    int state;
32
    int new_state;
33

    
34
    QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
35
    QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
36
    QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
37
} BDRVBlkdebugState;
38

    
39
typedef struct BlkdebugAIOCB {
40
    BlockDriverAIOCB common;
41
    QEMUBH *bh;
42
    int ret;
43
} BlkdebugAIOCB;
44

    
45
typedef struct BlkdebugSuspendedReq {
46
    Coroutine *co;
47
    char *tag;
48
    QLIST_ENTRY(BlkdebugSuspendedReq) next;
49
} BlkdebugSuspendedReq;
50

    
51
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
52

    
53
static const AIOCBInfo blkdebug_aiocb_info = {
54
    .aiocb_size = sizeof(BlkdebugAIOCB),
55
    .cancel     = blkdebug_aio_cancel,
56
};
57

    
58
enum {
59
    ACTION_INJECT_ERROR,
60
    ACTION_SET_STATE,
61
    ACTION_SUSPEND,
62
};
63

    
64
typedef struct BlkdebugRule {
65
    BlkDebugEvent event;
66
    int action;
67
    int state;
68
    union {
69
        struct {
70
            int error;
71
            int immediately;
72
            int once;
73
            int64_t sector;
74
        } inject;
75
        struct {
76
            int new_state;
77
        } set_state;
78
        struct {
79
            char *tag;
80
        } suspend;
81
    } options;
82
    QLIST_ENTRY(BlkdebugRule) next;
83
    QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
84
} BlkdebugRule;
85

    
86
static QemuOptsList inject_error_opts = {
87
    .name = "inject-error",
88
    .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
89
    .desc = {
90
        {
91
            .name = "event",
92
            .type = QEMU_OPT_STRING,
93
        },
94
        {
95
            .name = "state",
96
            .type = QEMU_OPT_NUMBER,
97
        },
98
        {
99
            .name = "errno",
100
            .type = QEMU_OPT_NUMBER,
101
        },
102
        {
103
            .name = "sector",
104
            .type = QEMU_OPT_NUMBER,
105
        },
106
        {
107
            .name = "once",
108
            .type = QEMU_OPT_BOOL,
109
        },
110
        {
111
            .name = "immediately",
112
            .type = QEMU_OPT_BOOL,
113
        },
114
        { /* end of list */ }
115
    },
116
};
117

    
118
static QemuOptsList set_state_opts = {
119
    .name = "set-state",
120
    .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
121
    .desc = {
122
        {
123
            .name = "event",
124
            .type = QEMU_OPT_STRING,
125
        },
126
        {
127
            .name = "state",
128
            .type = QEMU_OPT_NUMBER,
129
        },
130
        {
131
            .name = "new_state",
132
            .type = QEMU_OPT_NUMBER,
133
        },
134
        { /* end of list */ }
135
    },
136
};
137

    
138
static QemuOptsList *config_groups[] = {
139
    &inject_error_opts,
140
    &set_state_opts,
141
    NULL
142
};
143

    
144
static const char *event_names[BLKDBG_EVENT_MAX] = {
145
    [BLKDBG_L1_UPDATE]                      = "l1_update",
146
    [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table",
147
    [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table",
148
    [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table",
149

    
150
    [BLKDBG_L2_LOAD]                        = "l2_load",
151
    [BLKDBG_L2_UPDATE]                      = "l2_update",
152
    [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed",
153
    [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read",
154
    [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write",
155

    
156
    [BLKDBG_READ_AIO]                       = "read_aio",
157
    [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio",
158
    [BLKDBG_READ_COMPRESSED]                = "read_compressed",
159

    
160
    [BLKDBG_WRITE_AIO]                      = "write_aio",
161
    [BLKDBG_WRITE_COMPRESSED]               = "write_compressed",
162

    
163
    [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load",
164
    [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save",
165

    
166
    [BLKDBG_COW_READ]                       = "cow_read",
167
    [BLKDBG_COW_WRITE]                      = "cow_write",
168

    
169
    [BLKDBG_REFTABLE_LOAD]                  = "reftable_load",
170
    [BLKDBG_REFTABLE_GROW]                  = "reftable_grow",
171
    [BLKDBG_REFTABLE_UPDATE]                = "reftable_update",
172

    
173
    [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load",
174
    [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update",
175
    [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part",
176
    [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc",
177
    [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup",
178
    [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write",
179
    [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks",
180
    [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table",
181
    [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table",
182

    
183
    [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc",
184
    [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes",
185
    [BLKDBG_CLUSTER_FREE]                   = "cluster_free",
186

    
187
    [BLKDBG_FLUSH_TO_OS]                    = "flush_to_os",
188
    [BLKDBG_FLUSH_TO_DISK]                  = "flush_to_disk",
189
};
190

    
191
static int get_event_by_name(const char *name, BlkDebugEvent *event)
192
{
193
    int i;
194

    
195
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
196
        if (!strcmp(event_names[i], name)) {
197
            *event = i;
198
            return 0;
199
        }
200
    }
201

    
202
    return -1;
203
}
204

    
205
struct add_rule_data {
206
    BDRVBlkdebugState *s;
207
    int action;
208
};
209

    
210
static int add_rule(QemuOpts *opts, void *opaque)
211
{
212
    struct add_rule_data *d = opaque;
213
    BDRVBlkdebugState *s = d->s;
214
    const char* event_name;
215
    BlkDebugEvent event;
216
    struct BlkdebugRule *rule;
217

    
218
    /* Find the right event for the rule */
219
    event_name = qemu_opt_get(opts, "event");
220
    if (!event_name || get_event_by_name(event_name, &event) < 0) {
221
        return -1;
222
    }
223

    
224
    /* Set attributes common for all actions */
225
    rule = g_malloc0(sizeof(*rule));
226
    *rule = (struct BlkdebugRule) {
227
        .event  = event,
228
        .action = d->action,
229
        .state  = qemu_opt_get_number(opts, "state", 0),
230
    };
231

    
232
    /* Parse action-specific options */
233
    switch (d->action) {
234
    case ACTION_INJECT_ERROR:
235
        rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
236
        rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
237
        rule->options.inject.immediately =
238
            qemu_opt_get_bool(opts, "immediately", 0);
239
        rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
240
        break;
241

    
242
    case ACTION_SET_STATE:
243
        rule->options.set_state.new_state =
244
            qemu_opt_get_number(opts, "new_state", 0);
245
        break;
246

    
247
    case ACTION_SUSPEND:
248
        rule->options.suspend.tag =
249
            g_strdup(qemu_opt_get(opts, "tag"));
250
        break;
251
    };
252

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

    
256
    return 0;
257
}
258

    
259
static void remove_rule(BlkdebugRule *rule)
260
{
261
    switch (rule->action) {
262
    case ACTION_INJECT_ERROR:
263
    case ACTION_SET_STATE:
264
        break;
265
    case ACTION_SUSPEND:
266
        g_free(rule->options.suspend.tag);
267
        break;
268
    }
269

    
270
    QLIST_REMOVE(rule, next);
271
    g_free(rule);
272
}
273

    
274
static int read_config(BDRVBlkdebugState *s, const char *filename)
275
{
276
    FILE *f;
277
    int ret;
278
    struct add_rule_data d;
279

    
280
    f = fopen(filename, "r");
281
    if (f == NULL) {
282
        return -errno;
283
    }
284

    
285
    ret = qemu_config_parse(f, config_groups, filename);
286
    if (ret < 0) {
287
        goto fail;
288
    }
289

    
290
    d.s = s;
291
    d.action = ACTION_INJECT_ERROR;
292
    qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0);
293

    
294
    d.action = ACTION_SET_STATE;
295
    qemu_opts_foreach(&set_state_opts, add_rule, &d, 0);
296

    
297
    ret = 0;
298
fail:
299
    qemu_opts_reset(&inject_error_opts);
300
    qemu_opts_reset(&set_state_opts);
301
    fclose(f);
302
    return ret;
303
}
304

    
305
/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
306
static void blkdebug_parse_filename(const char *filename, QDict *options,
307
                                    Error **errp)
308
{
309
    const char *c;
310

    
311
    /* Parse the blkdebug: prefix */
312
    if (!strstart(filename, "blkdebug:", &filename)) {
313
        error_setg(errp, "File name string must start with 'blkdebug:'");
314
        return;
315
    }
316

    
317
    /* Parse config file path */
318
    c = strchr(filename, ':');
319
    if (c == NULL) {
320
        error_setg(errp, "blkdebug requires both config file and image path");
321
        return;
322
    }
323

    
324
    if (c != filename) {
325
        QString *config_path;
326
        config_path = qstring_from_substr(filename, 0, c - filename - 1);
327
        qdict_put(options, "config", config_path);
328
    }
329

    
330
    /* TODO Allow multi-level nesting and set file.filename here */
331
    filename = c + 1;
332
    qdict_put(options, "x-image", qstring_from_str(filename));
333
}
334

    
335
static QemuOptsList runtime_opts = {
336
    .name = "blkdebug",
337
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
338
    .desc = {
339
        {
340
            .name = "config",
341
            .type = QEMU_OPT_STRING,
342
            .help = "Path to the configuration file",
343
        },
344
        {
345
            .name = "x-image",
346
            .type = QEMU_OPT_STRING,
347
            .help = "[internal use only, will be removed]",
348
        },
349
        { /* end of list */ }
350
    },
351
};
352

    
353
static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
354
                         Error **errp)
355
{
356
    BDRVBlkdebugState *s = bs->opaque;
357
    QemuOpts *opts;
358
    Error *local_err = NULL;
359
    const char *filename, *config;
360
    int ret;
361

    
362
    opts = qemu_opts_create_nofail(&runtime_opts);
363
    qemu_opts_absorb_qdict(opts, options, &local_err);
364
    if (error_is_set(&local_err)) {
365
        qerror_report_err(local_err);
366
        error_free(local_err);
367
        ret = -EINVAL;
368
        goto fail;
369
    }
370

    
371
    /* Read rules from config file */
372
    config = qemu_opt_get(opts, "config");
373
    if (config) {
374
        ret = read_config(s, config);
375
        if (ret < 0) {
376
            goto fail;
377
        }
378
    }
379

    
380
    /* Set initial state */
381
    s->state = 1;
382

    
383
    /* Open the backing file */
384
    filename = qemu_opt_get(opts, "x-image");
385
    if (filename == NULL) {
386
        ret = -EINVAL;
387
        goto fail;
388
    }
389

    
390
    ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
391
    if (ret < 0) {
392
        qerror_report_err(local_err);
393
        error_free(local_err);
394
        goto fail;
395
    }
396

    
397
    ret = 0;
398
fail:
399
    qemu_opts_del(opts);
400
    return ret;
401
}
402

    
403
static void error_callback_bh(void *opaque)
404
{
405
    struct BlkdebugAIOCB *acb = opaque;
406
    qemu_bh_delete(acb->bh);
407
    acb->common.cb(acb->common.opaque, acb->ret);
408
    qemu_aio_release(acb);
409
}
410

    
411
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
412
{
413
    BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
414
    qemu_aio_release(acb);
415
}
416

    
417
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
418
    BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
419
{
420
    BDRVBlkdebugState *s = bs->opaque;
421
    int error = rule->options.inject.error;
422
    struct BlkdebugAIOCB *acb;
423
    QEMUBH *bh;
424

    
425
    if (rule->options.inject.once) {
426
        QSIMPLEQ_INIT(&s->active_rules);
427
    }
428

    
429
    if (rule->options.inject.immediately) {
430
        return NULL;
431
    }
432

    
433
    acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
434
    acb->ret = -error;
435

    
436
    bh = qemu_bh_new(error_callback_bh, acb);
437
    acb->bh = bh;
438
    qemu_bh_schedule(bh);
439

    
440
    return &acb->common;
441
}
442

    
443
static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
444
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
445
    BlockDriverCompletionFunc *cb, void *opaque)
446
{
447
    BDRVBlkdebugState *s = bs->opaque;
448
    BlkdebugRule *rule = NULL;
449

    
450
    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
451
        if (rule->options.inject.sector == -1 ||
452
            (rule->options.inject.sector >= sector_num &&
453
             rule->options.inject.sector < sector_num + nb_sectors)) {
454
            break;
455
        }
456
    }
457

    
458
    if (rule && rule->options.inject.error) {
459
        return inject_error(bs, cb, opaque, rule);
460
    }
461

    
462
    return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
463
}
464

    
465
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
466
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
467
    BlockDriverCompletionFunc *cb, void *opaque)
468
{
469
    BDRVBlkdebugState *s = bs->opaque;
470
    BlkdebugRule *rule = NULL;
471

    
472
    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
473
        if (rule->options.inject.sector == -1 ||
474
            (rule->options.inject.sector >= sector_num &&
475
             rule->options.inject.sector < sector_num + nb_sectors)) {
476
            break;
477
        }
478
    }
479

    
480
    if (rule && rule->options.inject.error) {
481
        return inject_error(bs, cb, opaque, rule);
482
    }
483

    
484
    return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
485
}
486

    
487

    
488
static void blkdebug_close(BlockDriverState *bs)
489
{
490
    BDRVBlkdebugState *s = bs->opaque;
491
    BlkdebugRule *rule, *next;
492
    int i;
493

    
494
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
495
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
496
            remove_rule(rule);
497
        }
498
    }
499
}
500

    
501
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
502
{
503
    BDRVBlkdebugState *s = bs->opaque;
504
    BlkdebugSuspendedReq r;
505

    
506
    r = (BlkdebugSuspendedReq) {
507
        .co         = qemu_coroutine_self(),
508
        .tag        = g_strdup(rule->options.suspend.tag),
509
    };
510

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

    
514
    printf("blkdebug: Suspended request '%s'\n", r.tag);
515
    qemu_coroutine_yield();
516
    printf("blkdebug: Resuming request '%s'\n", r.tag);
517

    
518
    QLIST_REMOVE(&r, next);
519
    g_free(r.tag);
520
}
521

    
522
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
523
    bool injected)
524
{
525
    BDRVBlkdebugState *s = bs->opaque;
526

    
527
    /* Only process rules for the current state */
528
    if (rule->state && rule->state != s->state) {
529
        return injected;
530
    }
531

    
532
    /* Take the action */
533
    switch (rule->action) {
534
    case ACTION_INJECT_ERROR:
535
        if (!injected) {
536
            QSIMPLEQ_INIT(&s->active_rules);
537
            injected = true;
538
        }
539
        QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
540
        break;
541

    
542
    case ACTION_SET_STATE:
543
        s->new_state = rule->options.set_state.new_state;
544
        break;
545

    
546
    case ACTION_SUSPEND:
547
        suspend_request(bs, rule);
548
        break;
549
    }
550
    return injected;
551
}
552

    
553
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
554
{
555
    BDRVBlkdebugState *s = bs->opaque;
556
    struct BlkdebugRule *rule, *next;
557
    bool injected;
558

    
559
    assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
560

    
561
    injected = false;
562
    s->new_state = s->state;
563
    QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
564
        injected = process_rule(bs, rule, injected);
565
    }
566
    s->state = s->new_state;
567
}
568

    
569
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
570
                                     const char *tag)
571
{
572
    BDRVBlkdebugState *s = bs->opaque;
573
    struct BlkdebugRule *rule;
574
    BlkDebugEvent blkdebug_event;
575

    
576
    if (get_event_by_name(event, &blkdebug_event) < 0) {
577
        return -ENOENT;
578
    }
579

    
580

    
581
    rule = g_malloc(sizeof(*rule));
582
    *rule = (struct BlkdebugRule) {
583
        .event  = blkdebug_event,
584
        .action = ACTION_SUSPEND,
585
        .state  = 0,
586
        .options.suspend.tag = g_strdup(tag),
587
    };
588

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

    
591
    return 0;
592
}
593

    
594
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
595
{
596
    BDRVBlkdebugState *s = bs->opaque;
597
    BlkdebugSuspendedReq *r;
598

    
599
    QLIST_FOREACH(r, &s->suspended_reqs, next) {
600
        if (!strcmp(r->tag, tag)) {
601
            qemu_coroutine_enter(r->co, NULL);
602
            return 0;
603
        }
604
    }
605
    return -ENOENT;
606
}
607

    
608

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

    
614
    QLIST_FOREACH(r, &s->suspended_reqs, next) {
615
        if (!strcmp(r->tag, tag)) {
616
            return true;
617
        }
618
    }
619
    return false;
620
}
621

    
622
static int64_t blkdebug_getlength(BlockDriverState *bs)
623
{
624
    return bdrv_getlength(bs->file);
625
}
626

    
627
static BlockDriver bdrv_blkdebug = {
628
    .format_name            = "blkdebug",
629
    .protocol_name          = "blkdebug",
630
    .instance_size          = sizeof(BDRVBlkdebugState),
631

    
632
    .bdrv_parse_filename    = blkdebug_parse_filename,
633
    .bdrv_file_open         = blkdebug_open,
634
    .bdrv_close             = blkdebug_close,
635
    .bdrv_getlength         = blkdebug_getlength,
636

    
637
    .bdrv_aio_readv         = blkdebug_aio_readv,
638
    .bdrv_aio_writev        = blkdebug_aio_writev,
639

    
640
    .bdrv_debug_event           = blkdebug_debug_event,
641
    .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
642
    .bdrv_debug_resume          = blkdebug_debug_resume,
643
    .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
644
};
645

    
646
static void bdrv_blkdebug_init(void)
647
{
648
    bdrv_register(&bdrv_blkdebug);
649
}
650

    
651
block_init(bdrv_blkdebug_init);