Statistics
| Branch: | Revision:

root / block / blkdebug.c @ 1de7afc9

History | View | Annotate | Download (15.8 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

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

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

    
187
static int get_event_by_name(const char *name, BlkDebugEvent *event)
188
{
189
    int i;
190

    
191
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
192
        if (!strcmp(event_names[i], name)) {
193
            *event = i;
194
            return 0;
195
        }
196
    }
197

    
198
    return -1;
199
}
200

    
201
struct add_rule_data {
202
    BDRVBlkdebugState *s;
203
    int action;
204
};
205

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

    
214
    /* Find the right event for the rule */
215
    event_name = qemu_opt_get(opts, "event");
216
    if (!event_name || get_event_by_name(event_name, &event) < 0) {
217
        return -1;
218
    }
219

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

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

    
238
    case ACTION_SET_STATE:
239
        rule->options.set_state.new_state =
240
            qemu_opt_get_number(opts, "new_state", 0);
241
        break;
242

    
243
    case ACTION_SUSPEND:
244
        rule->options.suspend.tag =
245
            g_strdup(qemu_opt_get(opts, "tag"));
246
        break;
247
    };
248

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

    
252
    return 0;
253
}
254

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

    
266
    QLIST_REMOVE(rule, next);
267
    g_free(rule);
268
}
269

    
270
static int read_config(BDRVBlkdebugState *s, const char *filename)
271
{
272
    FILE *f;
273
    int ret;
274
    struct add_rule_data d;
275

    
276
    /* Allow usage without config file */
277
    if (!*filename) {
278
        return 0;
279
    }
280

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

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

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

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

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

    
306
/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
307
static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
308
{
309
    BDRVBlkdebugState *s = bs->opaque;
310
    int ret;
311
    char *config, *c;
312

    
313
    /* Parse the blkdebug: prefix */
314
    if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) {
315
        return -EINVAL;
316
    }
317
    filename += strlen("blkdebug:");
318

    
319
    /* Read rules from config file */
320
    c = strchr(filename, ':');
321
    if (c == NULL) {
322
        return -EINVAL;
323
    }
324

    
325
    config = g_strdup(filename);
326
    config[c - filename] = '\0';
327
    ret = read_config(s, config);
328
    g_free(config);
329
    if (ret < 0) {
330
        return ret;
331
    }
332
    filename = c + 1;
333

    
334
    /* Set initial state */
335
    s->state = 1;
336

    
337
    /* Open the backing file */
338
    ret = bdrv_file_open(&bs->file, filename, flags);
339
    if (ret < 0) {
340
        return ret;
341
    }
342

    
343
    return 0;
344
}
345

    
346
static void error_callback_bh(void *opaque)
347
{
348
    struct BlkdebugAIOCB *acb = opaque;
349
    qemu_bh_delete(acb->bh);
350
    acb->common.cb(acb->common.opaque, acb->ret);
351
    qemu_aio_release(acb);
352
}
353

    
354
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
355
{
356
    BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
357
    qemu_aio_release(acb);
358
}
359

    
360
static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
361
    BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
362
{
363
    BDRVBlkdebugState *s = bs->opaque;
364
    int error = rule->options.inject.error;
365
    struct BlkdebugAIOCB *acb;
366
    QEMUBH *bh;
367

    
368
    if (rule->options.inject.once) {
369
        QSIMPLEQ_INIT(&s->active_rules);
370
    }
371

    
372
    if (rule->options.inject.immediately) {
373
        return NULL;
374
    }
375

    
376
    acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
377
    acb->ret = -error;
378

    
379
    bh = qemu_bh_new(error_callback_bh, acb);
380
    acb->bh = bh;
381
    qemu_bh_schedule(bh);
382

    
383
    return &acb->common;
384
}
385

    
386
static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
387
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
388
    BlockDriverCompletionFunc *cb, void *opaque)
389
{
390
    BDRVBlkdebugState *s = bs->opaque;
391
    BlkdebugRule *rule = NULL;
392

    
393
    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
394
        if (rule->options.inject.sector == -1 ||
395
            (rule->options.inject.sector >= sector_num &&
396
             rule->options.inject.sector < sector_num + nb_sectors)) {
397
            break;
398
        }
399
    }
400

    
401
    if (rule && rule->options.inject.error) {
402
        return inject_error(bs, cb, opaque, rule);
403
    }
404

    
405
    return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
406
}
407

    
408
static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
409
    int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
410
    BlockDriverCompletionFunc *cb, void *opaque)
411
{
412
    BDRVBlkdebugState *s = bs->opaque;
413
    BlkdebugRule *rule = NULL;
414

    
415
    QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
416
        if (rule->options.inject.sector == -1 ||
417
            (rule->options.inject.sector >= sector_num &&
418
             rule->options.inject.sector < sector_num + nb_sectors)) {
419
            break;
420
        }
421
    }
422

    
423
    if (rule && rule->options.inject.error) {
424
        return inject_error(bs, cb, opaque, rule);
425
    }
426

    
427
    return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
428
}
429

    
430

    
431
static void blkdebug_close(BlockDriverState *bs)
432
{
433
    BDRVBlkdebugState *s = bs->opaque;
434
    BlkdebugRule *rule, *next;
435
    int i;
436

    
437
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
438
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
439
            remove_rule(rule);
440
        }
441
    }
442
}
443

    
444
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
445
{
446
    BDRVBlkdebugState *s = bs->opaque;
447
    BlkdebugSuspendedReq r;
448

    
449
    r = (BlkdebugSuspendedReq) {
450
        .co         = qemu_coroutine_self(),
451
        .tag        = g_strdup(rule->options.suspend.tag),
452
    };
453

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

    
457
    printf("blkdebug: Suspended request '%s'\n", r.tag);
458
    qemu_coroutine_yield();
459
    printf("blkdebug: Resuming request '%s'\n", r.tag);
460

    
461
    QLIST_REMOVE(&r, next);
462
    g_free(r.tag);
463
}
464

    
465
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
466
    bool injected)
467
{
468
    BDRVBlkdebugState *s = bs->opaque;
469

    
470
    /* Only process rules for the current state */
471
    if (rule->state && rule->state != s->state) {
472
        return injected;
473
    }
474

    
475
    /* Take the action */
476
    switch (rule->action) {
477
    case ACTION_INJECT_ERROR:
478
        if (!injected) {
479
            QSIMPLEQ_INIT(&s->active_rules);
480
            injected = true;
481
        }
482
        QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
483
        break;
484

    
485
    case ACTION_SET_STATE:
486
        s->new_state = rule->options.set_state.new_state;
487
        break;
488

    
489
    case ACTION_SUSPEND:
490
        suspend_request(bs, rule);
491
        break;
492
    }
493
    return injected;
494
}
495

    
496
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
497
{
498
    BDRVBlkdebugState *s = bs->opaque;
499
    struct BlkdebugRule *rule, *next;
500
    bool injected;
501

    
502
    assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
503

    
504
    injected = false;
505
    s->new_state = s->state;
506
    QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
507
        injected = process_rule(bs, rule, injected);
508
    }
509
    s->state = s->new_state;
510
}
511

    
512
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
513
                                     const char *tag)
514
{
515
    BDRVBlkdebugState *s = bs->opaque;
516
    struct BlkdebugRule *rule;
517
    BlkDebugEvent blkdebug_event;
518

    
519
    if (get_event_by_name(event, &blkdebug_event) < 0) {
520
        return -ENOENT;
521
    }
522

    
523

    
524
    rule = g_malloc(sizeof(*rule));
525
    *rule = (struct BlkdebugRule) {
526
        .event  = blkdebug_event,
527
        .action = ACTION_SUSPEND,
528
        .state  = 0,
529
        .options.suspend.tag = g_strdup(tag),
530
    };
531

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

    
534
    return 0;
535
}
536

    
537
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
538
{
539
    BDRVBlkdebugState *s = bs->opaque;
540
    BlkdebugSuspendedReq *r;
541

    
542
    QLIST_FOREACH(r, &s->suspended_reqs, next) {
543
        if (!strcmp(r->tag, tag)) {
544
            qemu_coroutine_enter(r->co, NULL);
545
            return 0;
546
        }
547
    }
548
    return -ENOENT;
549
}
550

    
551

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

    
557
    QLIST_FOREACH(r, &s->suspended_reqs, next) {
558
        if (!strcmp(r->tag, tag)) {
559
            return true;
560
        }
561
    }
562
    return false;
563
}
564

    
565
static int64_t blkdebug_getlength(BlockDriverState *bs)
566
{
567
    return bdrv_getlength(bs->file);
568
}
569

    
570
static BlockDriver bdrv_blkdebug = {
571
    .format_name        = "blkdebug",
572
    .protocol_name      = "blkdebug",
573

    
574
    .instance_size      = sizeof(BDRVBlkdebugState),
575

    
576
    .bdrv_file_open     = blkdebug_open,
577
    .bdrv_close         = blkdebug_close,
578
    .bdrv_getlength     = blkdebug_getlength,
579

    
580
    .bdrv_aio_readv     = blkdebug_aio_readv,
581
    .bdrv_aio_writev    = blkdebug_aio_writev,
582

    
583
    .bdrv_debug_event           = blkdebug_debug_event,
584
    .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
585
    .bdrv_debug_resume          = blkdebug_debug_resume,
586
    .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
587
};
588

    
589
static void bdrv_blkdebug_init(void)
590
{
591
    bdrv_register(&bdrv_blkdebug);
592
}
593

    
594
block_init(bdrv_blkdebug_init);