xref: /openbmc/qemu/block/blkdebug.c (revision 15faf946)
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 "block_int.h"
27 #include "module.h"
28 
29 typedef struct BDRVBlkdebugState {
30     int state;
31     int new_state;
32 
33     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
34     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
35     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
36 } BDRVBlkdebugState;
37 
38 typedef struct BlkdebugAIOCB {
39     BlockDriverAIOCB common;
40     QEMUBH *bh;
41     int ret;
42 } BlkdebugAIOCB;
43 
44 typedef struct BlkdebugSuspendedReq {
45     Coroutine *co;
46     char *tag;
47     QLIST_ENTRY(BlkdebugSuspendedReq) next;
48 } BlkdebugSuspendedReq;
49 
50 static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
51 
52 static const AIOCBInfo blkdebug_aiocb_info = {
53     .aiocb_size = sizeof(BlkdebugAIOCB),
54     .cancel     = blkdebug_aio_cancel,
55 };
56 
57 enum {
58     ACTION_INJECT_ERROR,
59     ACTION_SET_STATE,
60     ACTION_SUSPEND,
61 };
62 
63 typedef struct BlkdebugRule {
64     BlkDebugEvent event;
65     int action;
66     int state;
67     union {
68         struct {
69             int error;
70             int immediately;
71             int once;
72             int64_t sector;
73         } inject;
74         struct {
75             int new_state;
76         } set_state;
77         struct {
78             char *tag;
79         } suspend;
80     } options;
81     QLIST_ENTRY(BlkdebugRule) next;
82     QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
83 } BlkdebugRule;
84 
85 static QemuOptsList inject_error_opts = {
86     .name = "inject-error",
87     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
88     .desc = {
89         {
90             .name = "event",
91             .type = QEMU_OPT_STRING,
92         },
93         {
94             .name = "state",
95             .type = QEMU_OPT_NUMBER,
96         },
97         {
98             .name = "errno",
99             .type = QEMU_OPT_NUMBER,
100         },
101         {
102             .name = "sector",
103             .type = QEMU_OPT_NUMBER,
104         },
105         {
106             .name = "once",
107             .type = QEMU_OPT_BOOL,
108         },
109         {
110             .name = "immediately",
111             .type = QEMU_OPT_BOOL,
112         },
113         { /* end of list */ }
114     },
115 };
116 
117 static QemuOptsList set_state_opts = {
118     .name = "set-state",
119     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
120     .desc = {
121         {
122             .name = "event",
123             .type = QEMU_OPT_STRING,
124         },
125         {
126             .name = "state",
127             .type = QEMU_OPT_NUMBER,
128         },
129         {
130             .name = "new_state",
131             .type = QEMU_OPT_NUMBER,
132         },
133         { /* end of list */ }
134     },
135 };
136 
137 static QemuOptsList *config_groups[] = {
138     &inject_error_opts,
139     &set_state_opts,
140     NULL
141 };
142 
143 static const char *event_names[BLKDBG_EVENT_MAX] = {
144     [BLKDBG_L1_UPDATE]                      = "l1_update",
145     [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table",
146     [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table",
147     [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table",
148 
149     [BLKDBG_L2_LOAD]                        = "l2_load",
150     [BLKDBG_L2_UPDATE]                      = "l2_update",
151     [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed",
152     [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read",
153     [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write",
154 
155     [BLKDBG_READ_AIO]                       = "read_aio",
156     [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio",
157     [BLKDBG_READ_COMPRESSED]                = "read_compressed",
158 
159     [BLKDBG_WRITE_AIO]                      = "write_aio",
160     [BLKDBG_WRITE_COMPRESSED]               = "write_compressed",
161 
162     [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load",
163     [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save",
164 
165     [BLKDBG_COW_READ]                       = "cow_read",
166     [BLKDBG_COW_WRITE]                      = "cow_write",
167 
168     [BLKDBG_REFTABLE_LOAD]                  = "reftable_load",
169     [BLKDBG_REFTABLE_GROW]                  = "reftable_grow",
170 
171     [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load",
172     [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update",
173     [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part",
174     [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc",
175     [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup",
176     [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write",
177     [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks",
178     [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table",
179     [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table",
180 
181     [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc",
182     [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes",
183     [BLKDBG_CLUSTER_FREE]                   = "cluster_free",
184 };
185 
186 static int get_event_by_name(const char *name, BlkDebugEvent *event)
187 {
188     int i;
189 
190     for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
191         if (!strcmp(event_names[i], name)) {
192             *event = i;
193             return 0;
194         }
195     }
196 
197     return -1;
198 }
199 
200 struct add_rule_data {
201     BDRVBlkdebugState *s;
202     int action;
203 };
204 
205 static int add_rule(QemuOpts *opts, void *opaque)
206 {
207     struct add_rule_data *d = opaque;
208     BDRVBlkdebugState *s = d->s;
209     const char* event_name;
210     BlkDebugEvent event;
211     struct BlkdebugRule *rule;
212 
213     /* Find the right event for the rule */
214     event_name = qemu_opt_get(opts, "event");
215     if (!event_name || get_event_by_name(event_name, &event) < 0) {
216         return -1;
217     }
218 
219     /* Set attributes common for all actions */
220     rule = g_malloc0(sizeof(*rule));
221     *rule = (struct BlkdebugRule) {
222         .event  = event,
223         .action = d->action,
224         .state  = qemu_opt_get_number(opts, "state", 0),
225     };
226 
227     /* Parse action-specific options */
228     switch (d->action) {
229     case ACTION_INJECT_ERROR:
230         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
231         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
232         rule->options.inject.immediately =
233             qemu_opt_get_bool(opts, "immediately", 0);
234         rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
235         break;
236 
237     case ACTION_SET_STATE:
238         rule->options.set_state.new_state =
239             qemu_opt_get_number(opts, "new_state", 0);
240         break;
241 
242     case ACTION_SUSPEND:
243         rule->options.suspend.tag =
244             g_strdup(qemu_opt_get(opts, "tag"));
245         break;
246     };
247 
248     /* Add the rule */
249     QLIST_INSERT_HEAD(&s->rules[event], rule, next);
250 
251     return 0;
252 }
253 
254 static void remove_rule(BlkdebugRule *rule)
255 {
256     switch (rule->action) {
257     case ACTION_INJECT_ERROR:
258     case ACTION_SET_STATE:
259         break;
260     case ACTION_SUSPEND:
261         g_free(rule->options.suspend.tag);
262         break;
263     }
264 
265     QLIST_REMOVE(rule, next);
266     g_free(rule);
267 }
268 
269 static int read_config(BDRVBlkdebugState *s, const char *filename)
270 {
271     FILE *f;
272     int ret;
273     struct add_rule_data d;
274 
275     /* Allow usage without config file */
276     if (!*filename) {
277         return 0;
278     }
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 int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
307 {
308     BDRVBlkdebugState *s = bs->opaque;
309     int ret;
310     char *config, *c;
311 
312     /* Parse the blkdebug: prefix */
313     if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) {
314         return -EINVAL;
315     }
316     filename += strlen("blkdebug:");
317 
318     /* Read rules from config file */
319     c = strchr(filename, ':');
320     if (c == NULL) {
321         return -EINVAL;
322     }
323 
324     config = g_strdup(filename);
325     config[c - filename] = '\0';
326     ret = read_config(s, config);
327     g_free(config);
328     if (ret < 0) {
329         return ret;
330     }
331     filename = c + 1;
332 
333     /* Set initial state */
334     s->state = 1;
335 
336     /* Open the backing file */
337     ret = bdrv_file_open(&bs->file, filename, flags);
338     if (ret < 0) {
339         return ret;
340     }
341 
342     return 0;
343 }
344 
345 static void error_callback_bh(void *opaque)
346 {
347     struct BlkdebugAIOCB *acb = opaque;
348     qemu_bh_delete(acb->bh);
349     acb->common.cb(acb->common.opaque, acb->ret);
350     qemu_aio_release(acb);
351 }
352 
353 static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
354 {
355     BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
356     qemu_aio_release(acb);
357 }
358 
359 static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
360     BlockDriverCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
361 {
362     BDRVBlkdebugState *s = bs->opaque;
363     int error = rule->options.inject.error;
364     struct BlkdebugAIOCB *acb;
365     QEMUBH *bh;
366 
367     if (rule->options.inject.once) {
368         QSIMPLEQ_INIT(&s->active_rules);
369     }
370 
371     if (rule->options.inject.immediately) {
372         return NULL;
373     }
374 
375     acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
376     acb->ret = -error;
377 
378     bh = qemu_bh_new(error_callback_bh, acb);
379     acb->bh = bh;
380     qemu_bh_schedule(bh);
381 
382     return &acb->common;
383 }
384 
385 static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
386     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
387     BlockDriverCompletionFunc *cb, void *opaque)
388 {
389     BDRVBlkdebugState *s = bs->opaque;
390     BlkdebugRule *rule = NULL;
391 
392     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
393         if (rule->options.inject.sector == -1 ||
394             (rule->options.inject.sector >= sector_num &&
395              rule->options.inject.sector < sector_num + nb_sectors)) {
396             break;
397         }
398     }
399 
400     if (rule && rule->options.inject.error) {
401         return inject_error(bs, cb, opaque, rule);
402     }
403 
404     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
405 }
406 
407 static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
408     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
409     BlockDriverCompletionFunc *cb, void *opaque)
410 {
411     BDRVBlkdebugState *s = bs->opaque;
412     BlkdebugRule *rule = NULL;
413 
414     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
415         if (rule->options.inject.sector == -1 ||
416             (rule->options.inject.sector >= sector_num &&
417              rule->options.inject.sector < sector_num + nb_sectors)) {
418             break;
419         }
420     }
421 
422     if (rule && rule->options.inject.error) {
423         return inject_error(bs, cb, opaque, rule);
424     }
425 
426     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
427 }
428 
429 
430 static void blkdebug_close(BlockDriverState *bs)
431 {
432     BDRVBlkdebugState *s = bs->opaque;
433     BlkdebugRule *rule, *next;
434     int i;
435 
436     for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
437         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
438             remove_rule(rule);
439         }
440     }
441 }
442 
443 static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
444 {
445     BDRVBlkdebugState *s = bs->opaque;
446     BlkdebugSuspendedReq r;
447 
448     r = (BlkdebugSuspendedReq) {
449         .co         = qemu_coroutine_self(),
450         .tag        = g_strdup(rule->options.suspend.tag),
451     };
452 
453     remove_rule(rule);
454     QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
455 
456     printf("blkdebug: Suspended request '%s'\n", r.tag);
457     qemu_coroutine_yield();
458     printf("blkdebug: Resuming request '%s'\n", r.tag);
459 
460     QLIST_REMOVE(&r, next);
461     g_free(r.tag);
462 }
463 
464 static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
465     bool injected)
466 {
467     BDRVBlkdebugState *s = bs->opaque;
468 
469     /* Only process rules for the current state */
470     if (rule->state && rule->state != s->state) {
471         return injected;
472     }
473 
474     /* Take the action */
475     switch (rule->action) {
476     case ACTION_INJECT_ERROR:
477         if (!injected) {
478             QSIMPLEQ_INIT(&s->active_rules);
479             injected = true;
480         }
481         QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
482         break;
483 
484     case ACTION_SET_STATE:
485         s->new_state = rule->options.set_state.new_state;
486         break;
487 
488     case ACTION_SUSPEND:
489         suspend_request(bs, rule);
490         break;
491     }
492     return injected;
493 }
494 
495 static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
496 {
497     BDRVBlkdebugState *s = bs->opaque;
498     struct BlkdebugRule *rule, *next;
499     bool injected;
500 
501     assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
502 
503     injected = false;
504     s->new_state = s->state;
505     QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
506         injected = process_rule(bs, rule, injected);
507     }
508     s->state = s->new_state;
509 }
510 
511 static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
512                                      const char *tag)
513 {
514     BDRVBlkdebugState *s = bs->opaque;
515     struct BlkdebugRule *rule;
516     BlkDebugEvent blkdebug_event;
517 
518     if (get_event_by_name(event, &blkdebug_event) < 0) {
519         return -ENOENT;
520     }
521 
522 
523     rule = g_malloc(sizeof(*rule));
524     *rule = (struct BlkdebugRule) {
525         .event  = blkdebug_event,
526         .action = ACTION_SUSPEND,
527         .state  = 0,
528         .options.suspend.tag = g_strdup(tag),
529     };
530 
531     QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
532 
533     return 0;
534 }
535 
536 static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
537 {
538     BDRVBlkdebugState *s = bs->opaque;
539     BlkdebugSuspendedReq *r;
540 
541     QLIST_FOREACH(r, &s->suspended_reqs, next) {
542         if (!strcmp(r->tag, tag)) {
543             qemu_coroutine_enter(r->co, NULL);
544             return 0;
545         }
546     }
547     return -ENOENT;
548 }
549 
550 
551 static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
552 {
553     BDRVBlkdebugState *s = bs->opaque;
554     BlkdebugSuspendedReq *r;
555 
556     QLIST_FOREACH(r, &s->suspended_reqs, next) {
557         if (!strcmp(r->tag, tag)) {
558             return true;
559         }
560     }
561     return false;
562 }
563 
564 static int64_t blkdebug_getlength(BlockDriverState *bs)
565 {
566     return bdrv_getlength(bs->file);
567 }
568 
569 static BlockDriver bdrv_blkdebug = {
570     .format_name        = "blkdebug",
571     .protocol_name      = "blkdebug",
572 
573     .instance_size      = sizeof(BDRVBlkdebugState),
574 
575     .bdrv_file_open     = blkdebug_open,
576     .bdrv_close         = blkdebug_close,
577     .bdrv_getlength     = blkdebug_getlength,
578 
579     .bdrv_aio_readv     = blkdebug_aio_readv,
580     .bdrv_aio_writev    = blkdebug_aio_writev,
581 
582     .bdrv_debug_event           = blkdebug_debug_event,
583     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
584     .bdrv_debug_resume          = blkdebug_debug_resume,
585     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
586 };
587 
588 static void bdrv_blkdebug_init(void)
589 {
590     bdrv_register(&bdrv_blkdebug);
591 }
592 
593 block_init(bdrv_blkdebug_init);
594