Version up
[sdk/emulator/qemu.git] / block / blkdebug.c
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/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu/cutils.h"
28 #include "qemu/config-file.h"
29 #include "block/block_int.h"
30 #include "qemu/module.h"
31 #include "qapi/qmp/qbool.h"
32 #include "qapi/qmp/qdict.h"
33 #include "qapi/qmp/qint.h"
34 #include "qapi/qmp/qstring.h"
35 #include "sysemu/qtest.h"
36
37 typedef struct BDRVBlkdebugState {
38     int state;
39     int new_state;
40     int align;
41
42     /* For blkdebug_refresh_filename() */
43     char *config_file;
44
45     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
46     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
47     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
48 } BDRVBlkdebugState;
49
50 typedef struct BlkdebugAIOCB {
51     BlockAIOCB common;
52     int ret;
53 } BlkdebugAIOCB;
54
55 typedef struct BlkdebugSuspendedReq {
56     Coroutine *co;
57     char *tag;
58     QLIST_ENTRY(BlkdebugSuspendedReq) next;
59 } BlkdebugSuspendedReq;
60
61 static const AIOCBInfo blkdebug_aiocb_info = {
62     .aiocb_size    = sizeof(BlkdebugAIOCB),
63 };
64
65 enum {
66     ACTION_INJECT_ERROR,
67     ACTION_SET_STATE,
68     ACTION_SUSPEND,
69 };
70
71 typedef struct BlkdebugRule {
72     BlkdebugEvent event;
73     int action;
74     int state;
75     union {
76         struct {
77             int error;
78             int immediately;
79             int once;
80             int64_t sector;
81         } inject;
82         struct {
83             int new_state;
84         } set_state;
85         struct {
86             char *tag;
87         } suspend;
88     } options;
89     QLIST_ENTRY(BlkdebugRule) next;
90     QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
91 } BlkdebugRule;
92
93 static QemuOptsList inject_error_opts = {
94     .name = "inject-error",
95     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
96     .desc = {
97         {
98             .name = "event",
99             .type = QEMU_OPT_STRING,
100         },
101         {
102             .name = "state",
103             .type = QEMU_OPT_NUMBER,
104         },
105         {
106             .name = "errno",
107             .type = QEMU_OPT_NUMBER,
108         },
109         {
110             .name = "sector",
111             .type = QEMU_OPT_NUMBER,
112         },
113         {
114             .name = "once",
115             .type = QEMU_OPT_BOOL,
116         },
117         {
118             .name = "immediately",
119             .type = QEMU_OPT_BOOL,
120         },
121         { /* end of list */ }
122     },
123 };
124
125 static QemuOptsList set_state_opts = {
126     .name = "set-state",
127     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
128     .desc = {
129         {
130             .name = "event",
131             .type = QEMU_OPT_STRING,
132         },
133         {
134             .name = "state",
135             .type = QEMU_OPT_NUMBER,
136         },
137         {
138             .name = "new_state",
139             .type = QEMU_OPT_NUMBER,
140         },
141         { /* end of list */ }
142     },
143 };
144
145 static QemuOptsList *config_groups[] = {
146     &inject_error_opts,
147     &set_state_opts,
148     NULL
149 };
150
151 static int get_event_by_name(const char *name, BlkdebugEvent *event)
152 {
153     int i;
154
155     for (i = 0; i < BLKDBG__MAX; i++) {
156         if (!strcmp(BlkdebugEvent_lookup[i], name)) {
157             *event = i;
158             return 0;
159         }
160     }
161
162     return -1;
163 }
164
165 struct add_rule_data {
166     BDRVBlkdebugState *s;
167     int action;
168 };
169
170 static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
171 {
172     struct add_rule_data *d = opaque;
173     BDRVBlkdebugState *s = d->s;
174     const char* event_name;
175     BlkdebugEvent event;
176     struct BlkdebugRule *rule;
177
178     /* Find the right event for the rule */
179     event_name = qemu_opt_get(opts, "event");
180     if (!event_name) {
181         error_setg(errp, "Missing event name for rule");
182         return -1;
183     } else if (get_event_by_name(event_name, &event) < 0) {
184         error_setg(errp, "Invalid event name \"%s\"", event_name);
185         return -1;
186     }
187
188     /* Set attributes common for all actions */
189     rule = g_malloc0(sizeof(*rule));
190     *rule = (struct BlkdebugRule) {
191         .event  = event,
192         .action = d->action,
193         .state  = qemu_opt_get_number(opts, "state", 0),
194     };
195
196     /* Parse action-specific options */
197     switch (d->action) {
198     case ACTION_INJECT_ERROR:
199         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
200         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
201         rule->options.inject.immediately =
202             qemu_opt_get_bool(opts, "immediately", 0);
203         rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
204         break;
205
206     case ACTION_SET_STATE:
207         rule->options.set_state.new_state =
208             qemu_opt_get_number(opts, "new_state", 0);
209         break;
210
211     case ACTION_SUSPEND:
212         rule->options.suspend.tag =
213             g_strdup(qemu_opt_get(opts, "tag"));
214         break;
215     };
216
217     /* Add the rule */
218     QLIST_INSERT_HEAD(&s->rules[event], rule, next);
219
220     return 0;
221 }
222
223 static void remove_rule(BlkdebugRule *rule)
224 {
225     switch (rule->action) {
226     case ACTION_INJECT_ERROR:
227     case ACTION_SET_STATE:
228         break;
229     case ACTION_SUSPEND:
230         g_free(rule->options.suspend.tag);
231         break;
232     }
233
234     QLIST_REMOVE(rule, next);
235     g_free(rule);
236 }
237
238 static int read_config(BDRVBlkdebugState *s, const char *filename,
239                        QDict *options, Error **errp)
240 {
241     FILE *f = NULL;
242     int ret;
243     struct add_rule_data d;
244     Error *local_err = NULL;
245
246     if (filename) {
247         f = fopen(filename, "r");
248         if (f == NULL) {
249             error_setg_errno(errp, errno, "Could not read blkdebug config file");
250             return -errno;
251         }
252
253         ret = qemu_config_parse(f, config_groups, filename);
254         if (ret < 0) {
255             error_setg(errp, "Could not parse blkdebug config file");
256             ret = -EINVAL;
257             goto fail;
258         }
259     }
260
261     qemu_config_parse_qdict(options, config_groups, &local_err);
262     if (local_err) {
263         error_propagate(errp, local_err);
264         ret = -EINVAL;
265         goto fail;
266     }
267
268     d.s = s;
269     d.action = ACTION_INJECT_ERROR;
270     qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
271     if (local_err) {
272         error_propagate(errp, local_err);
273         ret = -EINVAL;
274         goto fail;
275     }
276
277     d.action = ACTION_SET_STATE;
278     qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
279     if (local_err) {
280         error_propagate(errp, local_err);
281         ret = -EINVAL;
282         goto fail;
283     }
284
285     ret = 0;
286 fail:
287     qemu_opts_reset(&inject_error_opts);
288     qemu_opts_reset(&set_state_opts);
289     if (f) {
290         fclose(f);
291     }
292     return ret;
293 }
294
295 /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
296 static void blkdebug_parse_filename(const char *filename, QDict *options,
297                                     Error **errp)
298 {
299     const char *c;
300
301     /* Parse the blkdebug: prefix */
302     if (!strstart(filename, "blkdebug:", &filename)) {
303         /* There was no prefix; therefore, all options have to be already
304            present in the QDict (except for the filename) */
305         qdict_put(options, "x-image", qstring_from_str(filename));
306         return;
307     }
308
309     /* Parse config file path */
310     c = strchr(filename, ':');
311     if (c == NULL) {
312         error_setg(errp, "blkdebug requires both config file and image path");
313         return;
314     }
315
316     if (c != filename) {
317         QString *config_path;
318         config_path = qstring_from_substr(filename, 0, c - filename - 1);
319         qdict_put(options, "config", config_path);
320     }
321
322     /* TODO Allow multi-level nesting and set file.filename here */
323     filename = c + 1;
324     qdict_put(options, "x-image", qstring_from_str(filename));
325 }
326
327 static QemuOptsList runtime_opts = {
328     .name = "blkdebug",
329     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
330     .desc = {
331         {
332             .name = "config",
333             .type = QEMU_OPT_STRING,
334             .help = "Path to the configuration file",
335         },
336         {
337             .name = "x-image",
338             .type = QEMU_OPT_STRING,
339             .help = "[internal use only, will be removed]",
340         },
341         {
342             .name = "align",
343             .type = QEMU_OPT_SIZE,
344             .help = "Required alignment in bytes",
345         },
346         { /* end of list */ }
347     },
348 };
349
350 static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
351                          Error **errp)
352 {
353     BDRVBlkdebugState *s = bs->opaque;
354     QemuOpts *opts;
355     Error *local_err = NULL;
356     uint64_t align;
357     int ret;
358
359     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
360     qemu_opts_absorb_qdict(opts, options, &local_err);
361     if (local_err) {
362         error_propagate(errp, local_err);
363         ret = -EINVAL;
364         goto out;
365     }
366
367     /* Read rules from config file or command line options */
368     s->config_file = g_strdup(qemu_opt_get(opts, "config"));
369     ret = read_config(s, s->config_file, options, errp);
370     if (ret) {
371         goto out;
372     }
373
374     /* Set initial state */
375     s->state = 1;
376
377     /* Open the image file */
378     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
379                                bs, &child_file, false, &local_err);
380     if (local_err) {
381         ret = -EINVAL;
382         error_propagate(errp, local_err);
383         goto out;
384     }
385
386     /* Set request alignment */
387     align = qemu_opt_get_size(opts, "align", 0);
388     if (align < INT_MAX && is_power_of_2(align)) {
389         s->align = align;
390     } else if (align) {
391         error_setg(errp, "Invalid alignment");
392         ret = -EINVAL;
393         goto fail_unref;
394     }
395
396     ret = 0;
397     goto out;
398
399 fail_unref:
400     bdrv_unref_child(bs, bs->file);
401 out:
402     if (ret < 0) {
403         g_free(s->config_file);
404     }
405     qemu_opts_del(opts);
406     return ret;
407 }
408
409 static void error_callback_bh(void *opaque)
410 {
411     struct BlkdebugAIOCB *acb = opaque;
412     acb->common.cb(acb->common.opaque, acb->ret);
413     qemu_aio_unref(acb);
414 }
415
416 static BlockAIOCB *inject_error(BlockDriverState *bs,
417     BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
418 {
419     BDRVBlkdebugState *s = bs->opaque;
420     int error = rule->options.inject.error;
421     struct BlkdebugAIOCB *acb;
422     bool immediately = rule->options.inject.immediately;
423
424     if (rule->options.inject.once) {
425         QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
426         remove_rule(rule);
427     }
428
429     if (immediately) {
430         return NULL;
431     }
432
433     acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
434     acb->ret = -error;
435
436     aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh, acb);
437
438     return &acb->common;
439 }
440
441 static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
442     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
443     BlockCompletionFunc *cb, void *opaque)
444 {
445     BDRVBlkdebugState *s = bs->opaque;
446     BlkdebugRule *rule = NULL;
447
448     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
449         if (rule->options.inject.sector == -1 ||
450             (rule->options.inject.sector >= sector_num &&
451              rule->options.inject.sector < sector_num + nb_sectors)) {
452             break;
453         }
454     }
455
456     if (rule && rule->options.inject.error) {
457         return inject_error(bs, cb, opaque, rule);
458     }
459
460     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors,
461                           cb, opaque);
462 }
463
464 static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
465     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
466     BlockCompletionFunc *cb, void *opaque)
467 {
468     BDRVBlkdebugState *s = bs->opaque;
469     BlkdebugRule *rule = NULL;
470
471     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
472         if (rule->options.inject.sector == -1 ||
473             (rule->options.inject.sector >= sector_num &&
474              rule->options.inject.sector < sector_num + nb_sectors)) {
475             break;
476         }
477     }
478
479     if (rule && rule->options.inject.error) {
480         return inject_error(bs, cb, opaque, rule);
481     }
482
483     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
484                            cb, opaque);
485 }
486
487 static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
488     BlockCompletionFunc *cb, void *opaque)
489 {
490     BDRVBlkdebugState *s = bs->opaque;
491     BlkdebugRule *rule = NULL;
492
493     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
494         if (rule->options.inject.sector == -1) {
495             break;
496         }
497     }
498
499     if (rule && rule->options.inject.error) {
500         return inject_error(bs, cb, opaque, rule);
501     }
502
503     return bdrv_aio_flush(bs->file->bs, cb, opaque);
504 }
505
506
507 static void blkdebug_close(BlockDriverState *bs)
508 {
509     BDRVBlkdebugState *s = bs->opaque;
510     BlkdebugRule *rule, *next;
511     int i;
512
513     for (i = 0; i < BLKDBG__MAX; i++) {
514         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
515             remove_rule(rule);
516         }
517     }
518
519     g_free(s->config_file);
520 }
521
522 static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
523 {
524     BDRVBlkdebugState *s = bs->opaque;
525     BlkdebugSuspendedReq r;
526
527     r = (BlkdebugSuspendedReq) {
528         .co         = qemu_coroutine_self(),
529         .tag        = g_strdup(rule->options.suspend.tag),
530     };
531
532     remove_rule(rule);
533     QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
534
535     if (!qtest_enabled()) {
536         printf("blkdebug: Suspended request '%s'\n", r.tag);
537     }
538     qemu_coroutine_yield();
539     if (!qtest_enabled()) {
540         printf("blkdebug: Resuming request '%s'\n", r.tag);
541     }
542
543     QLIST_REMOVE(&r, next);
544     g_free(r.tag);
545 }
546
547 static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
548     bool injected)
549 {
550     BDRVBlkdebugState *s = bs->opaque;
551
552     /* Only process rules for the current state */
553     if (rule->state && rule->state != s->state) {
554         return injected;
555     }
556
557     /* Take the action */
558     switch (rule->action) {
559     case ACTION_INJECT_ERROR:
560         if (!injected) {
561             QSIMPLEQ_INIT(&s->active_rules);
562             injected = true;
563         }
564         QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
565         break;
566
567     case ACTION_SET_STATE:
568         s->new_state = rule->options.set_state.new_state;
569         break;
570
571     case ACTION_SUSPEND:
572         suspend_request(bs, rule);
573         break;
574     }
575     return injected;
576 }
577
578 static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
579 {
580     BDRVBlkdebugState *s = bs->opaque;
581     struct BlkdebugRule *rule, *next;
582     bool injected;
583
584     assert((int)event >= 0 && event < BLKDBG__MAX);
585
586     injected = false;
587     s->new_state = s->state;
588     QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
589         injected = process_rule(bs, rule, injected);
590     }
591     s->state = s->new_state;
592 }
593
594 static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
595                                      const char *tag)
596 {
597     BDRVBlkdebugState *s = bs->opaque;
598     struct BlkdebugRule *rule;
599     BlkdebugEvent blkdebug_event;
600
601     if (get_event_by_name(event, &blkdebug_event) < 0) {
602         return -ENOENT;
603     }
604
605
606     rule = g_malloc(sizeof(*rule));
607     *rule = (struct BlkdebugRule) {
608         .event  = blkdebug_event,
609         .action = ACTION_SUSPEND,
610         .state  = 0,
611         .options.suspend.tag = g_strdup(tag),
612     };
613
614     QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
615
616     return 0;
617 }
618
619 static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
620 {
621     BDRVBlkdebugState *s = bs->opaque;
622     BlkdebugSuspendedReq *r, *next;
623
624     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
625         if (!strcmp(r->tag, tag)) {
626             qemu_coroutine_enter(r->co);
627             return 0;
628         }
629     }
630     return -ENOENT;
631 }
632
633 static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
634                                             const char *tag)
635 {
636     BDRVBlkdebugState *s = bs->opaque;
637     BlkdebugSuspendedReq *r, *r_next;
638     BlkdebugRule *rule, *next;
639     int i, ret = -ENOENT;
640
641     for (i = 0; i < BLKDBG__MAX; i++) {
642         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
643             if (rule->action == ACTION_SUSPEND &&
644                 !strcmp(rule->options.suspend.tag, tag)) {
645                 remove_rule(rule);
646                 ret = 0;
647             }
648         }
649     }
650     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
651         if (!strcmp(r->tag, tag)) {
652             qemu_coroutine_enter(r->co);
653             ret = 0;
654         }
655     }
656     return ret;
657 }
658
659 static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
660 {
661     BDRVBlkdebugState *s = bs->opaque;
662     BlkdebugSuspendedReq *r;
663
664     QLIST_FOREACH(r, &s->suspended_reqs, next) {
665         if (!strcmp(r->tag, tag)) {
666             return true;
667         }
668     }
669     return false;
670 }
671
672 static int64_t blkdebug_getlength(BlockDriverState *bs)
673 {
674     return bdrv_getlength(bs->file->bs);
675 }
676
677 static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
678 {
679     return bdrv_truncate(bs->file->bs, offset);
680 }
681
682 static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
683 {
684     BDRVBlkdebugState *s = bs->opaque;
685     QDict *opts;
686     const QDictEntry *e;
687     bool force_json = false;
688
689     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
690         if (strcmp(qdict_entry_key(e), "config") &&
691             strcmp(qdict_entry_key(e), "x-image"))
692         {
693             force_json = true;
694             break;
695         }
696     }
697
698     if (force_json && !bs->file->bs->full_open_options) {
699         /* The config file cannot be recreated, so creating a plain filename
700          * is impossible */
701         return;
702     }
703
704     if (!force_json && bs->file->bs->exact_filename[0]) {
705         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
706                  "blkdebug:%s:%s", s->config_file ?: "",
707                  bs->file->bs->exact_filename);
708     }
709
710     opts = qdict_new();
711     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
712
713     QINCREF(bs->file->bs->full_open_options);
714     qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
715
716     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
717         if (strcmp(qdict_entry_key(e), "x-image")) {
718             qobject_incref(qdict_entry_value(e));
719             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
720         }
721     }
722
723     bs->full_open_options = opts;
724 }
725
726 static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
727 {
728     BDRVBlkdebugState *s = bs->opaque;
729
730     if (s->align) {
731         bs->bl.request_alignment = s->align;
732     }
733 }
734
735 static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
736                                    BlockReopenQueue *queue, Error **errp)
737 {
738     return 0;
739 }
740
741 static BlockDriver bdrv_blkdebug = {
742     .format_name            = "blkdebug",
743     .protocol_name          = "blkdebug",
744     .instance_size          = sizeof(BDRVBlkdebugState),
745
746     .bdrv_parse_filename    = blkdebug_parse_filename,
747     .bdrv_file_open         = blkdebug_open,
748     .bdrv_close             = blkdebug_close,
749     .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
750     .bdrv_getlength         = blkdebug_getlength,
751     .bdrv_truncate          = blkdebug_truncate,
752     .bdrv_refresh_filename  = blkdebug_refresh_filename,
753     .bdrv_refresh_limits    = blkdebug_refresh_limits,
754
755     .bdrv_aio_readv         = blkdebug_aio_readv,
756     .bdrv_aio_writev        = blkdebug_aio_writev,
757     .bdrv_aio_flush         = blkdebug_aio_flush,
758
759     .bdrv_debug_event           = blkdebug_debug_event,
760     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
761     .bdrv_debug_remove_breakpoint
762                                 = blkdebug_debug_remove_breakpoint,
763     .bdrv_debug_resume          = blkdebug_debug_resume,
764     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
765 };
766
767 static void bdrv_blkdebug_init(void)
768 {
769     bdrv_register(&bdrv_blkdebug);
770 }
771
772 block_init(bdrv_blkdebug_init);