blkdebug: Simplify override logic
[qemu/ar7.git] / block / blkdebug.c
blob29ab7a3a8bcf64139e892a2c0bb11906aef6cc72
1 /*
2 * Block protocol for I/O error injection
4 * Copyright (C) 2016-2017 Red Hat, Inc.
5 * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
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 #include "qapi/qmp/qbool.h"
33 #include "qapi/qmp/qdict.h"
34 #include "qapi/qmp/qint.h"
35 #include "qapi/qmp/qstring.h"
36 #include "sysemu/qtest.h"
38 typedef struct BDRVBlkdebugState {
39 int state;
40 int new_state;
41 uint64_t align;
43 /* For blkdebug_refresh_filename() */
44 char *config_file;
46 QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
47 QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
48 QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
49 } BDRVBlkdebugState;
51 typedef struct BlkdebugAIOCB {
52 BlockAIOCB common;
53 int ret;
54 } BlkdebugAIOCB;
56 typedef struct BlkdebugSuspendedReq {
57 Coroutine *co;
58 char *tag;
59 QLIST_ENTRY(BlkdebugSuspendedReq) next;
60 } BlkdebugSuspendedReq;
62 enum {
63 ACTION_INJECT_ERROR,
64 ACTION_SET_STATE,
65 ACTION_SUSPEND,
68 typedef struct BlkdebugRule {
69 BlkdebugEvent event;
70 int action;
71 int state;
72 union {
73 struct {
74 int error;
75 int immediately;
76 int once;
77 int64_t offset;
78 } inject;
79 struct {
80 int new_state;
81 } set_state;
82 struct {
83 char *tag;
84 } suspend;
85 } options;
86 QLIST_ENTRY(BlkdebugRule) next;
87 QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
88 } BlkdebugRule;
90 static QemuOptsList inject_error_opts = {
91 .name = "inject-error",
92 .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
93 .desc = {
95 .name = "event",
96 .type = QEMU_OPT_STRING,
99 .name = "state",
100 .type = QEMU_OPT_NUMBER,
103 .name = "errno",
104 .type = QEMU_OPT_NUMBER,
107 .name = "sector",
108 .type = QEMU_OPT_NUMBER,
111 .name = "once",
112 .type = QEMU_OPT_BOOL,
115 .name = "immediately",
116 .type = QEMU_OPT_BOOL,
118 { /* end of list */ }
122 static QemuOptsList set_state_opts = {
123 .name = "set-state",
124 .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
125 .desc = {
127 .name = "event",
128 .type = QEMU_OPT_STRING,
131 .name = "state",
132 .type = QEMU_OPT_NUMBER,
135 .name = "new_state",
136 .type = QEMU_OPT_NUMBER,
138 { /* end of list */ }
142 static QemuOptsList *config_groups[] = {
143 &inject_error_opts,
144 &set_state_opts,
145 NULL
148 static int get_event_by_name(const char *name, BlkdebugEvent *event)
150 int i;
152 for (i = 0; i < BLKDBG__MAX; i++) {
153 if (!strcmp(BlkdebugEvent_lookup[i], name)) {
154 *event = i;
155 return 0;
159 return -1;
162 struct add_rule_data {
163 BDRVBlkdebugState *s;
164 int action;
167 static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
169 struct add_rule_data *d = opaque;
170 BDRVBlkdebugState *s = d->s;
171 const char* event_name;
172 BlkdebugEvent event;
173 struct BlkdebugRule *rule;
174 int64_t sector;
176 /* Find the right event for the rule */
177 event_name = qemu_opt_get(opts, "event");
178 if (!event_name) {
179 error_setg(errp, "Missing event name for rule");
180 return -1;
181 } else if (get_event_by_name(event_name, &event) < 0) {
182 error_setg(errp, "Invalid event name \"%s\"", event_name);
183 return -1;
186 /* Set attributes common for all actions */
187 rule = g_malloc0(sizeof(*rule));
188 *rule = (struct BlkdebugRule) {
189 .event = event,
190 .action = d->action,
191 .state = qemu_opt_get_number(opts, "state", 0),
194 /* Parse action-specific options */
195 switch (d->action) {
196 case ACTION_INJECT_ERROR:
197 rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
198 rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
199 rule->options.inject.immediately =
200 qemu_opt_get_bool(opts, "immediately", 0);
201 sector = qemu_opt_get_number(opts, "sector", -1);
202 rule->options.inject.offset =
203 sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
204 break;
206 case ACTION_SET_STATE:
207 rule->options.set_state.new_state =
208 qemu_opt_get_number(opts, "new_state", 0);
209 break;
211 case ACTION_SUSPEND:
212 rule->options.suspend.tag =
213 g_strdup(qemu_opt_get(opts, "tag"));
214 break;
217 /* Add the rule */
218 QLIST_INSERT_HEAD(&s->rules[event], rule, next);
220 return 0;
223 static void remove_rule(BlkdebugRule *rule)
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;
234 QLIST_REMOVE(rule, next);
235 g_free(rule);
238 static int read_config(BDRVBlkdebugState *s, const char *filename,
239 QDict *options, Error **errp)
241 FILE *f = NULL;
242 int ret;
243 struct add_rule_data d;
244 Error *local_err = NULL;
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;
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;
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;
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;
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;
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);
292 return ret;
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)
299 const char *c;
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_str(options, "x-image", filename);
306 return;
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;
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);
322 /* TODO Allow multi-level nesting and set file.filename here */
323 filename = c + 1;
324 qdict_put_str(options, "x-image", filename);
327 static QemuOptsList runtime_opts = {
328 .name = "blkdebug",
329 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
330 .desc = {
332 .name = "config",
333 .type = QEMU_OPT_STRING,
334 .help = "Path to the configuration file",
337 .name = "x-image",
338 .type = QEMU_OPT_STRING,
339 .help = "[internal use only, will be removed]",
342 .name = "align",
343 .type = QEMU_OPT_SIZE,
344 .help = "Required alignment in bytes",
346 { /* end of list */ }
350 static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
351 Error **errp)
353 BDRVBlkdebugState *s = bs->opaque;
354 QemuOpts *opts;
355 Error *local_err = NULL;
356 int ret;
358 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
359 qemu_opts_absorb_qdict(opts, options, &local_err);
360 if (local_err) {
361 error_propagate(errp, local_err);
362 ret = -EINVAL;
363 goto out;
366 /* Read rules from config file or command line options */
367 s->config_file = g_strdup(qemu_opt_get(opts, "config"));
368 ret = read_config(s, s->config_file, options, errp);
369 if (ret) {
370 goto out;
373 /* Set initial state */
374 s->state = 1;
376 /* Open the image file */
377 bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
378 bs, &child_file, false, &local_err);
379 if (local_err) {
380 ret = -EINVAL;
381 error_propagate(errp, local_err);
382 goto out;
385 bs->supported_write_flags = BDRV_REQ_FUA &
386 bs->file->bs->supported_write_flags;
387 bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
388 bs->file->bs->supported_zero_flags;
389 ret = -EINVAL;
391 /* Set request alignment */
392 s->align = qemu_opt_get_size(opts, "align", 0);
393 if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
394 error_setg(errp, "Cannot meet constraints with align %" PRIu64,
395 s->align);
396 goto out;
399 ret = 0;
400 out:
401 if (ret < 0) {
402 g_free(s->config_file);
404 qemu_opts_del(opts);
405 return ret;
408 static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
410 BDRVBlkdebugState *s = bs->opaque;
411 BlkdebugRule *rule = NULL;
412 int error;
413 bool immediately;
415 QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
416 uint64_t inject_offset = rule->options.inject.offset;
418 if (inject_offset == -1 ||
419 (bytes && inject_offset >= offset &&
420 inject_offset < offset + bytes))
422 break;
426 if (!rule || !rule->options.inject.error) {
427 return 0;
430 immediately = rule->options.inject.immediately;
431 error = rule->options.inject.error;
433 if (rule->options.inject.once) {
434 QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
435 remove_rule(rule);
438 if (!immediately) {
439 aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
440 qemu_coroutine_yield();
443 return -error;
446 static int coroutine_fn
447 blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
448 QEMUIOVector *qiov, int flags)
450 int err;
452 /* Sanity check block layer guarantees */
453 assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
454 assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
455 if (bs->bl.max_transfer) {
456 assert(bytes <= bs->bl.max_transfer);
459 err = rule_check(bs, offset, bytes);
460 if (err) {
461 return err;
464 return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
467 static int coroutine_fn
468 blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
469 QEMUIOVector *qiov, int flags)
471 int err;
473 /* Sanity check block layer guarantees */
474 assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
475 assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
476 if (bs->bl.max_transfer) {
477 assert(bytes <= bs->bl.max_transfer);
480 err = rule_check(bs, offset, bytes);
481 if (err) {
482 return err;
485 return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
488 static int blkdebug_co_flush(BlockDriverState *bs)
490 int err = rule_check(bs, 0, 0);
492 if (err) {
493 return err;
496 return bdrv_co_flush(bs->file->bs);
499 static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
500 int64_t offset, int count,
501 BdrvRequestFlags flags)
503 uint32_t align = MAX(bs->bl.request_alignment,
504 bs->bl.pwrite_zeroes_alignment);
505 int err;
507 /* Only pass through requests that are larger than requested
508 * preferred alignment (so that we test the fallback to writes on
509 * unaligned portions), and check that the block layer never hands
510 * us anything unaligned that crosses an alignment boundary. */
511 if (count < align) {
512 assert(QEMU_IS_ALIGNED(offset, align) ||
513 QEMU_IS_ALIGNED(offset + count, align) ||
514 DIV_ROUND_UP(offset, align) ==
515 DIV_ROUND_UP(offset + count, align));
516 return -ENOTSUP;
518 assert(QEMU_IS_ALIGNED(offset, align));
519 assert(QEMU_IS_ALIGNED(count, align));
520 if (bs->bl.max_pwrite_zeroes) {
521 assert(count <= bs->bl.max_pwrite_zeroes);
524 err = rule_check(bs, offset, count);
525 if (err) {
526 return err;
529 return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags);
532 static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
533 int64_t offset, int count)
535 uint32_t align = bs->bl.pdiscard_alignment;
536 int err;
538 /* Only pass through requests that are larger than requested
539 * minimum alignment, and ensure that unaligned requests do not
540 * cross optimum discard boundaries. */
541 if (count < bs->bl.request_alignment) {
542 assert(QEMU_IS_ALIGNED(offset, align) ||
543 QEMU_IS_ALIGNED(offset + count, align) ||
544 DIV_ROUND_UP(offset, align) ==
545 DIV_ROUND_UP(offset + count, align));
546 return -ENOTSUP;
548 assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
549 assert(QEMU_IS_ALIGNED(count, bs->bl.request_alignment));
550 if (align && count >= align) {
551 assert(QEMU_IS_ALIGNED(offset, align));
552 assert(QEMU_IS_ALIGNED(count, align));
554 if (bs->bl.max_pdiscard) {
555 assert(count <= bs->bl.max_pdiscard);
558 err = rule_check(bs, offset, count);
559 if (err) {
560 return err;
563 return bdrv_co_pdiscard(bs->file->bs, offset, count);
566 static void blkdebug_close(BlockDriverState *bs)
568 BDRVBlkdebugState *s = bs->opaque;
569 BlkdebugRule *rule, *next;
570 int i;
572 for (i = 0; i < BLKDBG__MAX; i++) {
573 QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
574 remove_rule(rule);
578 g_free(s->config_file);
581 static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
583 BDRVBlkdebugState *s = bs->opaque;
584 BlkdebugSuspendedReq r;
586 r = (BlkdebugSuspendedReq) {
587 .co = qemu_coroutine_self(),
588 .tag = g_strdup(rule->options.suspend.tag),
591 remove_rule(rule);
592 QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
594 if (!qtest_enabled()) {
595 printf("blkdebug: Suspended request '%s'\n", r.tag);
597 qemu_coroutine_yield();
598 if (!qtest_enabled()) {
599 printf("blkdebug: Resuming request '%s'\n", r.tag);
602 QLIST_REMOVE(&r, next);
603 g_free(r.tag);
606 static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
607 bool injected)
609 BDRVBlkdebugState *s = bs->opaque;
611 /* Only process rules for the current state */
612 if (rule->state && rule->state != s->state) {
613 return injected;
616 /* Take the action */
617 switch (rule->action) {
618 case ACTION_INJECT_ERROR:
619 if (!injected) {
620 QSIMPLEQ_INIT(&s->active_rules);
621 injected = true;
623 QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
624 break;
626 case ACTION_SET_STATE:
627 s->new_state = rule->options.set_state.new_state;
628 break;
630 case ACTION_SUSPEND:
631 suspend_request(bs, rule);
632 break;
634 return injected;
637 static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
639 BDRVBlkdebugState *s = bs->opaque;
640 struct BlkdebugRule *rule, *next;
641 bool injected;
643 assert((int)event >= 0 && event < BLKDBG__MAX);
645 injected = false;
646 s->new_state = s->state;
647 QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
648 injected = process_rule(bs, rule, injected);
650 s->state = s->new_state;
653 static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
654 const char *tag)
656 BDRVBlkdebugState *s = bs->opaque;
657 struct BlkdebugRule *rule;
658 BlkdebugEvent blkdebug_event;
660 if (get_event_by_name(event, &blkdebug_event) < 0) {
661 return -ENOENT;
665 rule = g_malloc(sizeof(*rule));
666 *rule = (struct BlkdebugRule) {
667 .event = blkdebug_event,
668 .action = ACTION_SUSPEND,
669 .state = 0,
670 .options.suspend.tag = g_strdup(tag),
673 QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
675 return 0;
678 static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
680 BDRVBlkdebugState *s = bs->opaque;
681 BlkdebugSuspendedReq *r, *next;
683 QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
684 if (!strcmp(r->tag, tag)) {
685 qemu_coroutine_enter(r->co);
686 return 0;
689 return -ENOENT;
692 static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
693 const char *tag)
695 BDRVBlkdebugState *s = bs->opaque;
696 BlkdebugSuspendedReq *r, *r_next;
697 BlkdebugRule *rule, *next;
698 int i, ret = -ENOENT;
700 for (i = 0; i < BLKDBG__MAX; i++) {
701 QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
702 if (rule->action == ACTION_SUSPEND &&
703 !strcmp(rule->options.suspend.tag, tag)) {
704 remove_rule(rule);
705 ret = 0;
709 QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
710 if (!strcmp(r->tag, tag)) {
711 qemu_coroutine_enter(r->co);
712 ret = 0;
715 return ret;
718 static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
720 BDRVBlkdebugState *s = bs->opaque;
721 BlkdebugSuspendedReq *r;
723 QLIST_FOREACH(r, &s->suspended_reqs, next) {
724 if (!strcmp(r->tag, tag)) {
725 return true;
728 return false;
731 static int64_t blkdebug_getlength(BlockDriverState *bs)
733 return bdrv_getlength(bs->file->bs);
736 static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
738 return bdrv_truncate(bs->file, offset, errp);
741 static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
743 BDRVBlkdebugState *s = bs->opaque;
744 QDict *opts;
745 const QDictEntry *e;
746 bool force_json = false;
748 for (e = qdict_first(options); e; e = qdict_next(options, e)) {
749 if (strcmp(qdict_entry_key(e), "config") &&
750 strcmp(qdict_entry_key(e), "x-image"))
752 force_json = true;
753 break;
757 if (force_json && !bs->file->bs->full_open_options) {
758 /* The config file cannot be recreated, so creating a plain filename
759 * is impossible */
760 return;
763 if (!force_json && bs->file->bs->exact_filename[0]) {
764 snprintf(bs->exact_filename, sizeof(bs->exact_filename),
765 "blkdebug:%s:%s", s->config_file ?: "",
766 bs->file->bs->exact_filename);
769 opts = qdict_new();
770 qdict_put_str(opts, "driver", "blkdebug");
772 QINCREF(bs->file->bs->full_open_options);
773 qdict_put(opts, "image", bs->file->bs->full_open_options);
775 for (e = qdict_first(options); e; e = qdict_next(options, e)) {
776 if (strcmp(qdict_entry_key(e), "x-image")) {
777 qobject_incref(qdict_entry_value(e));
778 qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
782 bs->full_open_options = opts;
785 static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
787 BDRVBlkdebugState *s = bs->opaque;
789 if (s->align) {
790 bs->bl.request_alignment = s->align;
794 static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
795 BlockReopenQueue *queue, Error **errp)
797 return 0;
800 static BlockDriver bdrv_blkdebug = {
801 .format_name = "blkdebug",
802 .protocol_name = "blkdebug",
803 .instance_size = sizeof(BDRVBlkdebugState),
805 .bdrv_parse_filename = blkdebug_parse_filename,
806 .bdrv_file_open = blkdebug_open,
807 .bdrv_close = blkdebug_close,
808 .bdrv_reopen_prepare = blkdebug_reopen_prepare,
809 .bdrv_child_perm = bdrv_filter_default_perms,
811 .bdrv_getlength = blkdebug_getlength,
812 .bdrv_truncate = blkdebug_truncate,
813 .bdrv_refresh_filename = blkdebug_refresh_filename,
814 .bdrv_refresh_limits = blkdebug_refresh_limits,
816 .bdrv_co_preadv = blkdebug_co_preadv,
817 .bdrv_co_pwritev = blkdebug_co_pwritev,
818 .bdrv_co_flush_to_disk = blkdebug_co_flush,
819 .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
820 .bdrv_co_pdiscard = blkdebug_co_pdiscard,
822 .bdrv_debug_event = blkdebug_debug_event,
823 .bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
824 .bdrv_debug_remove_breakpoint
825 = blkdebug_debug_remove_breakpoint,
826 .bdrv_debug_resume = blkdebug_debug_resume,
827 .bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
830 static void bdrv_blkdebug_init(void)
832 bdrv_register(&bdrv_blkdebug);
835 block_init(bdrv_blkdebug_init);