xref: /qemu/block/blkdebug.c (revision 4cdd01d3)
16a143727SKevin Wolf /*
26a143727SKevin Wolf  * Block protocol for I/O error injection
36a143727SKevin Wolf  *
46a143727SKevin Wolf  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
56a143727SKevin Wolf  *
66a143727SKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
76a143727SKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
86a143727SKevin Wolf  * in the Software without restriction, including without limitation the rights
96a143727SKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
106a143727SKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
116a143727SKevin Wolf  * furnished to do so, subject to the following conditions:
126a143727SKevin Wolf  *
136a143727SKevin Wolf  * The above copyright notice and this permission notice shall be included in
146a143727SKevin Wolf  * all copies or substantial portions of the Software.
156a143727SKevin Wolf  *
166a143727SKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
176a143727SKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186a143727SKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
196a143727SKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
206a143727SKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
216a143727SKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
226a143727SKevin Wolf  * THE SOFTWARE.
236a143727SKevin Wolf  */
246a143727SKevin Wolf 
256a143727SKevin Wolf #include "qemu-common.h"
261de7afc9SPaolo Bonzini #include "qemu/config-file.h"
27737e150eSPaolo Bonzini #include "block/block_int.h"
281de7afc9SPaolo Bonzini #include "qemu/module.h"
292c31b04cSMax Reitz #include "qapi/qmp/qbool.h"
302c31b04cSMax Reitz #include "qapi/qmp/qdict.h"
312c31b04cSMax Reitz #include "qapi/qmp/qint.h"
322c31b04cSMax Reitz #include "qapi/qmp/qstring.h"
3320873526SMichael S. Tsirkin #include "sysemu/qtest.h"
346a143727SKevin Wolf 
356a143727SKevin Wolf typedef struct BDRVBlkdebugState {
36571cd43eSPaolo Bonzini     int state;
378f96b5beSPaolo Bonzini     int new_state;
383c90c65dSKevin Wolf 
397fb1cf16SEric Blake     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
40571cd43eSPaolo Bonzini     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
413c90c65dSKevin Wolf     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
426a143727SKevin Wolf } BDRVBlkdebugState;
436a143727SKevin Wolf 
44b9f66d96SKevin Wolf typedef struct BlkdebugAIOCB {
457c84b1b8SMarkus Armbruster     BlockAIOCB common;
46b9f66d96SKevin Wolf     QEMUBH *bh;
47b9f66d96SKevin Wolf     int ret;
48b9f66d96SKevin Wolf } BlkdebugAIOCB;
49b9f66d96SKevin Wolf 
503c90c65dSKevin Wolf typedef struct BlkdebugSuspendedReq {
513c90c65dSKevin Wolf     Coroutine *co;
523c90c65dSKevin Wolf     char *tag;
533c90c65dSKevin Wolf     QLIST_ENTRY(BlkdebugSuspendedReq) next;
543c90c65dSKevin Wolf } BlkdebugSuspendedReq;
553c90c65dSKevin Wolf 
56d7331bedSStefan Hajnoczi static const AIOCBInfo blkdebug_aiocb_info = {
57b9f66d96SKevin Wolf     .aiocb_size    = sizeof(BlkdebugAIOCB),
58b9f66d96SKevin Wolf };
59b9f66d96SKevin Wolf 
608b9b0cc2SKevin Wolf enum {
618b9b0cc2SKevin Wolf     ACTION_INJECT_ERROR,
628b9b0cc2SKevin Wolf     ACTION_SET_STATE,
633c90c65dSKevin Wolf     ACTION_SUSPEND,
648b9b0cc2SKevin Wolf };
658b9b0cc2SKevin Wolf 
668b9b0cc2SKevin Wolf typedef struct BlkdebugRule {
67a31939e6SEric Blake     BlkdebugEvent event;
688b9b0cc2SKevin Wolf     int action;
698b9b0cc2SKevin Wolf     int state;
708b9b0cc2SKevin Wolf     union {
718b9b0cc2SKevin Wolf         struct {
728b9b0cc2SKevin Wolf             int error;
738b9b0cc2SKevin Wolf             int immediately;
748b9b0cc2SKevin Wolf             int once;
75e4780db4SPaolo Bonzini             int64_t sector;
768b9b0cc2SKevin Wolf         } inject;
778b9b0cc2SKevin Wolf         struct {
788b9b0cc2SKevin Wolf             int new_state;
798b9b0cc2SKevin Wolf         } set_state;
803c90c65dSKevin Wolf         struct {
813c90c65dSKevin Wolf             char *tag;
823c90c65dSKevin Wolf         } suspend;
838b9b0cc2SKevin Wolf     } options;
848b9b0cc2SKevin Wolf     QLIST_ENTRY(BlkdebugRule) next;
85571cd43eSPaolo Bonzini     QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
868b9b0cc2SKevin Wolf } BlkdebugRule;
878b9b0cc2SKevin Wolf 
888b9b0cc2SKevin Wolf static QemuOptsList inject_error_opts = {
898b9b0cc2SKevin Wolf     .name = "inject-error",
908b9b0cc2SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
918b9b0cc2SKevin Wolf     .desc = {
928b9b0cc2SKevin Wolf         {
938b9b0cc2SKevin Wolf             .name = "event",
948b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
958b9b0cc2SKevin Wolf         },
968b9b0cc2SKevin Wolf         {
978b9b0cc2SKevin Wolf             .name = "state",
988b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
998b9b0cc2SKevin Wolf         },
1008b9b0cc2SKevin Wolf         {
1018b9b0cc2SKevin Wolf             .name = "errno",
1028b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1038b9b0cc2SKevin Wolf         },
1048b9b0cc2SKevin Wolf         {
105e4780db4SPaolo Bonzini             .name = "sector",
106e4780db4SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
107e4780db4SPaolo Bonzini         },
108e4780db4SPaolo Bonzini         {
1098b9b0cc2SKevin Wolf             .name = "once",
1108b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1118b9b0cc2SKevin Wolf         },
1128b9b0cc2SKevin Wolf         {
1138b9b0cc2SKevin Wolf             .name = "immediately",
1148b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1158b9b0cc2SKevin Wolf         },
1168b9b0cc2SKevin Wolf         { /* end of list */ }
1178b9b0cc2SKevin Wolf     },
1188b9b0cc2SKevin Wolf };
1198b9b0cc2SKevin Wolf 
1208b9b0cc2SKevin Wolf static QemuOptsList set_state_opts = {
1218b9b0cc2SKevin Wolf     .name = "set-state",
122327cdad4SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
1238b9b0cc2SKevin Wolf     .desc = {
1248b9b0cc2SKevin Wolf         {
1258b9b0cc2SKevin Wolf             .name = "event",
1268b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
1278b9b0cc2SKevin Wolf         },
1288b9b0cc2SKevin Wolf         {
1298b9b0cc2SKevin Wolf             .name = "state",
1308b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1318b9b0cc2SKevin Wolf         },
1328b9b0cc2SKevin Wolf         {
1338b9b0cc2SKevin Wolf             .name = "new_state",
1348b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1358b9b0cc2SKevin Wolf         },
1368b9b0cc2SKevin Wolf         { /* end of list */ }
1378b9b0cc2SKevin Wolf     },
1388b9b0cc2SKevin Wolf };
1398b9b0cc2SKevin Wolf 
1408b9b0cc2SKevin Wolf static QemuOptsList *config_groups[] = {
1418b9b0cc2SKevin Wolf     &inject_error_opts,
1428b9b0cc2SKevin Wolf     &set_state_opts,
1438b9b0cc2SKevin Wolf     NULL
1448b9b0cc2SKevin Wolf };
1458b9b0cc2SKevin Wolf 
146a31939e6SEric Blake static int get_event_by_name(const char *name, BlkdebugEvent *event)
1478b9b0cc2SKevin Wolf {
1488b9b0cc2SKevin Wolf     int i;
1498b9b0cc2SKevin Wolf 
1507fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
151a31939e6SEric Blake         if (!strcmp(BlkdebugEvent_lookup[i], name)) {
1528b9b0cc2SKevin Wolf             *event = i;
1538b9b0cc2SKevin Wolf             return 0;
1548b9b0cc2SKevin Wolf         }
1558b9b0cc2SKevin Wolf     }
1568b9b0cc2SKevin Wolf 
1578b9b0cc2SKevin Wolf     return -1;
1588b9b0cc2SKevin Wolf }
1598b9b0cc2SKevin Wolf 
1608b9b0cc2SKevin Wolf struct add_rule_data {
1618b9b0cc2SKevin Wolf     BDRVBlkdebugState *s;
1628b9b0cc2SKevin Wolf     int action;
1638b9b0cc2SKevin Wolf };
1648b9b0cc2SKevin Wolf 
16528d0de7aSMarkus Armbruster static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
1668b9b0cc2SKevin Wolf {
1678b9b0cc2SKevin Wolf     struct add_rule_data *d = opaque;
1688b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = d->s;
1698b9b0cc2SKevin Wolf     const char* event_name;
170a31939e6SEric Blake     BlkdebugEvent event;
1718b9b0cc2SKevin Wolf     struct BlkdebugRule *rule;
1728b9b0cc2SKevin Wolf 
1738b9b0cc2SKevin Wolf     /* Find the right event for the rule */
1748b9b0cc2SKevin Wolf     event_name = qemu_opt_get(opts, "event");
175d4362d64SStefan Hajnoczi     if (!event_name) {
1768809cfc3SMarkus Armbruster         error_setg(errp, "Missing event name for rule");
177d4362d64SStefan Hajnoczi         return -1;
178d4362d64SStefan Hajnoczi     } else if (get_event_by_name(event_name, &event) < 0) {
1798809cfc3SMarkus Armbruster         error_setg(errp, "Invalid event name \"%s\"", event_name);
1808b9b0cc2SKevin Wolf         return -1;
1818b9b0cc2SKevin Wolf     }
1828b9b0cc2SKevin Wolf 
1838b9b0cc2SKevin Wolf     /* Set attributes common for all actions */
1847267c094SAnthony Liguori     rule = g_malloc0(sizeof(*rule));
1858b9b0cc2SKevin Wolf     *rule = (struct BlkdebugRule) {
1868b9b0cc2SKevin Wolf         .event  = event,
1878b9b0cc2SKevin Wolf         .action = d->action,
1888b9b0cc2SKevin Wolf         .state  = qemu_opt_get_number(opts, "state", 0),
1898b9b0cc2SKevin Wolf     };
1908b9b0cc2SKevin Wolf 
1918b9b0cc2SKevin Wolf     /* Parse action-specific options */
1928b9b0cc2SKevin Wolf     switch (d->action) {
1938b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
1948b9b0cc2SKevin Wolf         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
1958b9b0cc2SKevin Wolf         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
1968b9b0cc2SKevin Wolf         rule->options.inject.immediately =
1978b9b0cc2SKevin Wolf             qemu_opt_get_bool(opts, "immediately", 0);
198e4780db4SPaolo Bonzini         rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1);
1998b9b0cc2SKevin Wolf         break;
2008b9b0cc2SKevin Wolf 
2018b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
2028b9b0cc2SKevin Wolf         rule->options.set_state.new_state =
2038b9b0cc2SKevin Wolf             qemu_opt_get_number(opts, "new_state", 0);
2048b9b0cc2SKevin Wolf         break;
2053c90c65dSKevin Wolf 
2063c90c65dSKevin Wolf     case ACTION_SUSPEND:
2073c90c65dSKevin Wolf         rule->options.suspend.tag =
2083c90c65dSKevin Wolf             g_strdup(qemu_opt_get(opts, "tag"));
2093c90c65dSKevin Wolf         break;
2108b9b0cc2SKevin Wolf     };
2118b9b0cc2SKevin Wolf 
2128b9b0cc2SKevin Wolf     /* Add the rule */
2138b9b0cc2SKevin Wolf     QLIST_INSERT_HEAD(&s->rules[event], rule, next);
2148b9b0cc2SKevin Wolf 
2158b9b0cc2SKevin Wolf     return 0;
2168b9b0cc2SKevin Wolf }
2178b9b0cc2SKevin Wolf 
2189e35542bSKevin Wolf static void remove_rule(BlkdebugRule *rule)
2199e35542bSKevin Wolf {
2209e35542bSKevin Wolf     switch (rule->action) {
2219e35542bSKevin Wolf     case ACTION_INJECT_ERROR:
2229e35542bSKevin Wolf     case ACTION_SET_STATE:
2239e35542bSKevin Wolf         break;
2243c90c65dSKevin Wolf     case ACTION_SUSPEND:
2253c90c65dSKevin Wolf         g_free(rule->options.suspend.tag);
2263c90c65dSKevin Wolf         break;
2279e35542bSKevin Wolf     }
2289e35542bSKevin Wolf 
2299e35542bSKevin Wolf     QLIST_REMOVE(rule, next);
2309e35542bSKevin Wolf     g_free(rule);
2319e35542bSKevin Wolf }
2329e35542bSKevin Wolf 
23389f2b21eSMax Reitz static int read_config(BDRVBlkdebugState *s, const char *filename,
23489f2b21eSMax Reitz                        QDict *options, Error **errp)
2358b9b0cc2SKevin Wolf {
23685a040e5SMax Reitz     FILE *f = NULL;
2378b9b0cc2SKevin Wolf     int ret;
2388b9b0cc2SKevin Wolf     struct add_rule_data d;
23989f2b21eSMax Reitz     Error *local_err = NULL;
2408b9b0cc2SKevin Wolf 
24185a040e5SMax Reitz     if (filename) {
2428b9b0cc2SKevin Wolf         f = fopen(filename, "r");
2438b9b0cc2SKevin Wolf         if (f == NULL) {
244466b49f2SMax Reitz             error_setg_errno(errp, errno, "Could not read blkdebug config file");
2458b9b0cc2SKevin Wolf             return -errno;
2468b9b0cc2SKevin Wolf         }
2478b9b0cc2SKevin Wolf 
2488b9b0cc2SKevin Wolf         ret = qemu_config_parse(f, config_groups, filename);
2498b9b0cc2SKevin Wolf         if (ret < 0) {
250466b49f2SMax Reitz             error_setg(errp, "Could not parse blkdebug config file");
251466b49f2SMax Reitz             ret = -EINVAL;
2528b9b0cc2SKevin Wolf             goto fail;
2538b9b0cc2SKevin Wolf         }
25485a040e5SMax Reitz     }
2558b9b0cc2SKevin Wolf 
25689f2b21eSMax Reitz     qemu_config_parse_qdict(options, config_groups, &local_err);
25784d18f06SMarkus Armbruster     if (local_err) {
25889f2b21eSMax Reitz         error_propagate(errp, local_err);
25989f2b21eSMax Reitz         ret = -EINVAL;
26089f2b21eSMax Reitz         goto fail;
26189f2b21eSMax Reitz     }
26289f2b21eSMax Reitz 
2638b9b0cc2SKevin Wolf     d.s = s;
2648b9b0cc2SKevin Wolf     d.action = ACTION_INJECT_ERROR;
2658809cfc3SMarkus Armbruster     qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
266d4362d64SStefan Hajnoczi     if (local_err) {
267d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
268d4362d64SStefan Hajnoczi         ret = -EINVAL;
269d4362d64SStefan Hajnoczi         goto fail;
270d4362d64SStefan Hajnoczi     }
2718b9b0cc2SKevin Wolf 
2728b9b0cc2SKevin Wolf     d.action = ACTION_SET_STATE;
2738809cfc3SMarkus Armbruster     qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
274d4362d64SStefan Hajnoczi     if (local_err) {
275d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
276d4362d64SStefan Hajnoczi         ret = -EINVAL;
277d4362d64SStefan Hajnoczi         goto fail;
278d4362d64SStefan Hajnoczi     }
2798b9b0cc2SKevin Wolf 
2808b9b0cc2SKevin Wolf     ret = 0;
2818b9b0cc2SKevin Wolf fail:
282698f0d52SKevin Wolf     qemu_opts_reset(&inject_error_opts);
283698f0d52SKevin Wolf     qemu_opts_reset(&set_state_opts);
28485a040e5SMax Reitz     if (f) {
2858b9b0cc2SKevin Wolf         fclose(f);
28685a040e5SMax Reitz     }
2878b9b0cc2SKevin Wolf     return ret;
2888b9b0cc2SKevin Wolf }
2898b9b0cc2SKevin Wolf 
2908b9b0cc2SKevin Wolf /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
291f4681212SKevin Wolf static void blkdebug_parse_filename(const char *filename, QDict *options,
292f4681212SKevin Wolf                                     Error **errp)
293f4681212SKevin Wolf {
294f4681212SKevin Wolf     const char *c;
295f4681212SKevin Wolf 
296f4681212SKevin Wolf     /* Parse the blkdebug: prefix */
297f4681212SKevin Wolf     if (!strstart(filename, "blkdebug:", &filename)) {
298d4881b9bSMax Reitz         /* There was no prefix; therefore, all options have to be already
299d4881b9bSMax Reitz            present in the QDict (except for the filename) */
300d4881b9bSMax Reitz         qdict_put(options, "x-image", qstring_from_str(filename));
301f4681212SKevin Wolf         return;
302f4681212SKevin Wolf     }
303f4681212SKevin Wolf 
304f4681212SKevin Wolf     /* Parse config file path */
305f4681212SKevin Wolf     c = strchr(filename, ':');
306f4681212SKevin Wolf     if (c == NULL) {
307f4681212SKevin Wolf         error_setg(errp, "blkdebug requires both config file and image path");
308f4681212SKevin Wolf         return;
309f4681212SKevin Wolf     }
310f4681212SKevin Wolf 
311f4681212SKevin Wolf     if (c != filename) {
312f4681212SKevin Wolf         QString *config_path;
313f4681212SKevin Wolf         config_path = qstring_from_substr(filename, 0, c - filename - 1);
314f4681212SKevin Wolf         qdict_put(options, "config", config_path);
315f4681212SKevin Wolf     }
316f4681212SKevin Wolf 
317f4681212SKevin Wolf     /* TODO Allow multi-level nesting and set file.filename here */
318f4681212SKevin Wolf     filename = c + 1;
319f4681212SKevin Wolf     qdict_put(options, "x-image", qstring_from_str(filename));
320f4681212SKevin Wolf }
321f4681212SKevin Wolf 
322f4681212SKevin Wolf static QemuOptsList runtime_opts = {
323f4681212SKevin Wolf     .name = "blkdebug",
324f4681212SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
325f4681212SKevin Wolf     .desc = {
326f4681212SKevin Wolf         {
327f4681212SKevin Wolf             .name = "config",
328f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
329f4681212SKevin Wolf             .help = "Path to the configuration file",
330f4681212SKevin Wolf         },
331f4681212SKevin Wolf         {
332f4681212SKevin Wolf             .name = "x-image",
333f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
334f4681212SKevin Wolf             .help = "[internal use only, will be removed]",
335f4681212SKevin Wolf         },
336b35ee7fbSKevin Wolf         {
337b35ee7fbSKevin Wolf             .name = "align",
338b35ee7fbSKevin Wolf             .type = QEMU_OPT_SIZE,
339b35ee7fbSKevin Wolf             .help = "Required alignment in bytes",
340b35ee7fbSKevin Wolf         },
341f4681212SKevin Wolf         { /* end of list */ }
342f4681212SKevin Wolf     },
343f4681212SKevin Wolf };
344f4681212SKevin Wolf 
345015a1036SMax Reitz static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
346015a1036SMax Reitz                          Error **errp)
3476a143727SKevin Wolf {
3486a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
349f4681212SKevin Wolf     QemuOpts *opts;
350f4681212SKevin Wolf     Error *local_err = NULL;
3514373593dSMax Reitz     const char *config;
352b35ee7fbSKevin Wolf     uint64_t align;
3538b9b0cc2SKevin Wolf     int ret;
3546a143727SKevin Wolf 
35587ea75d5SPeter Crosthwaite     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
356f4681212SKevin Wolf     qemu_opts_absorb_qdict(opts, options, &local_err);
35784d18f06SMarkus Armbruster     if (local_err) {
35810ffa72fSMax Reitz         error_propagate(errp, local_err);
359f4681212SKevin Wolf         ret = -EINVAL;
360eaf944a4SKevin Wolf         goto out;
3616a143727SKevin Wolf     }
3626a143727SKevin Wolf 
36389f2b21eSMax Reitz     /* Read rules from config file or command line options */
364f4681212SKevin Wolf     config = qemu_opt_get(opts, "config");
36589f2b21eSMax Reitz     ret = read_config(s, config, options, errp);
366466b49f2SMax Reitz     if (ret) {
367eaf944a4SKevin Wolf         goto out;
3688b9b0cc2SKevin Wolf     }
3698b9b0cc2SKevin Wolf 
3708db520ceSKevin Wolf     /* Set initial state */
371571cd43eSPaolo Bonzini     s->state = 1;
3728db520ceSKevin Wolf 
3736b826af7SFam Zheng     /* Open the image file */
3749a4f4c31SKevin Wolf     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
375f3930ed0SKevin Wolf                                bs, &child_file, false, &local_err);
3769a4f4c31SKevin Wolf     if (local_err) {
3779a4f4c31SKevin Wolf         ret = -EINVAL;
37810ffa72fSMax Reitz         error_propagate(errp, local_err);
379eaf944a4SKevin Wolf         goto out;
380f4681212SKevin Wolf     }
381f4681212SKevin Wolf 
382b35ee7fbSKevin Wolf     /* Set request alignment */
383b35ee7fbSKevin Wolf     align = qemu_opt_get_size(opts, "align", bs->request_alignment);
384b35ee7fbSKevin Wolf     if (align > 0 && align < INT_MAX && !(align & (align - 1))) {
385b35ee7fbSKevin Wolf         bs->request_alignment = align;
386b35ee7fbSKevin Wolf     } else {
387b35ee7fbSKevin Wolf         error_setg(errp, "Invalid alignment");
388b35ee7fbSKevin Wolf         ret = -EINVAL;
389eaf944a4SKevin Wolf         goto fail_unref;
390b35ee7fbSKevin Wolf     }
391b35ee7fbSKevin Wolf 
392f4681212SKevin Wolf     ret = 0;
393eaf944a4SKevin Wolf     goto out;
394eaf944a4SKevin Wolf 
395eaf944a4SKevin Wolf fail_unref:
3969a4f4c31SKevin Wolf     bdrv_unref_child(bs, bs->file);
397eaf944a4SKevin Wolf out:
398f4681212SKevin Wolf     qemu_opts_del(opts);
399f4681212SKevin Wolf     return ret;
4006a143727SKevin Wolf }
4016a143727SKevin Wolf 
402b9f66d96SKevin Wolf static void error_callback_bh(void *opaque)
403b9f66d96SKevin Wolf {
404b9f66d96SKevin Wolf     struct BlkdebugAIOCB *acb = opaque;
405b9f66d96SKevin Wolf     qemu_bh_delete(acb->bh);
406b9f66d96SKevin Wolf     acb->common.cb(acb->common.opaque, acb->ret);
4078007429aSFam Zheng     qemu_aio_unref(acb);
408b9f66d96SKevin Wolf }
409b9f66d96SKevin Wolf 
4107c84b1b8SMarkus Armbruster static BlockAIOCB *inject_error(BlockDriverState *bs,
411097310b5SMarkus Armbruster     BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule)
412b9f66d96SKevin Wolf {
413b9f66d96SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
414571cd43eSPaolo Bonzini     int error = rule->options.inject.error;
415b9f66d96SKevin Wolf     struct BlkdebugAIOCB *acb;
416b9f66d96SKevin Wolf     QEMUBH *bh;
417a069e2f1SJohn Snow     bool immediately = rule->options.inject.immediately;
418b9f66d96SKevin Wolf 
419571cd43eSPaolo Bonzini     if (rule->options.inject.once) {
420a069e2f1SJohn Snow         QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
421a069e2f1SJohn Snow         remove_rule(rule);
422b9f66d96SKevin Wolf     }
423b9f66d96SKevin Wolf 
424a069e2f1SJohn Snow     if (immediately) {
425b9f66d96SKevin Wolf         return NULL;
426b9f66d96SKevin Wolf     }
427b9f66d96SKevin Wolf 
428d7331bedSStefan Hajnoczi     acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque);
429b9f66d96SKevin Wolf     acb->ret = -error;
430b9f66d96SKevin Wolf 
4317e1efdf0SStefan Hajnoczi     bh = aio_bh_new(bdrv_get_aio_context(bs), error_callback_bh, acb);
432b9f66d96SKevin Wolf     acb->bh = bh;
433b9f66d96SKevin Wolf     qemu_bh_schedule(bh);
434b9f66d96SKevin Wolf 
435b666d239SKevin Wolf     return &acb->common;
436b9f66d96SKevin Wolf }
437b9f66d96SKevin Wolf 
4387c84b1b8SMarkus Armbruster static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
4396a143727SKevin Wolf     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
440097310b5SMarkus Armbruster     BlockCompletionFunc *cb, void *opaque)
4416a143727SKevin Wolf {
4426a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
443e4780db4SPaolo Bonzini     BlkdebugRule *rule = NULL;
444e4780db4SPaolo Bonzini 
445e4780db4SPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
446e4780db4SPaolo Bonzini         if (rule->options.inject.sector == -1 ||
447e4780db4SPaolo Bonzini             (rule->options.inject.sector >= sector_num &&
448e4780db4SPaolo Bonzini              rule->options.inject.sector < sector_num + nb_sectors)) {
449e4780db4SPaolo Bonzini             break;
450e4780db4SPaolo Bonzini         }
451e4780db4SPaolo Bonzini     }
452b9f66d96SKevin Wolf 
453571cd43eSPaolo Bonzini     if (rule && rule->options.inject.error) {
454571cd43eSPaolo Bonzini         return inject_error(bs, cb, opaque, rule);
455b9f66d96SKevin Wolf     }
456b9f66d96SKevin Wolf 
4579a4f4c31SKevin Wolf     return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors,
4589a4f4c31SKevin Wolf                           cb, opaque);
4596a143727SKevin Wolf }
4606a143727SKevin Wolf 
4617c84b1b8SMarkus Armbruster static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
4626a143727SKevin Wolf     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
463097310b5SMarkus Armbruster     BlockCompletionFunc *cb, void *opaque)
4646a143727SKevin Wolf {
4656a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
466e4780db4SPaolo Bonzini     BlkdebugRule *rule = NULL;
467e4780db4SPaolo Bonzini 
468e4780db4SPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
469e4780db4SPaolo Bonzini         if (rule->options.inject.sector == -1 ||
470e4780db4SPaolo Bonzini             (rule->options.inject.sector >= sector_num &&
471e4780db4SPaolo Bonzini              rule->options.inject.sector < sector_num + nb_sectors)) {
472e4780db4SPaolo Bonzini             break;
473e4780db4SPaolo Bonzini         }
474e4780db4SPaolo Bonzini     }
475b9f66d96SKevin Wolf 
476571cd43eSPaolo Bonzini     if (rule && rule->options.inject.error) {
477571cd43eSPaolo Bonzini         return inject_error(bs, cb, opaque, rule);
478b9f66d96SKevin Wolf     }
479b9f66d96SKevin Wolf 
4809a4f4c31SKevin Wolf     return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors,
4819a4f4c31SKevin Wolf                            cb, opaque);
4826a143727SKevin Wolf }
4836a143727SKevin Wolf 
4847c84b1b8SMarkus Armbruster static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
485097310b5SMarkus Armbruster     BlockCompletionFunc *cb, void *opaque)
4869e52c53bSPaolo Bonzini {
4879e52c53bSPaolo Bonzini     BDRVBlkdebugState *s = bs->opaque;
4889e52c53bSPaolo Bonzini     BlkdebugRule *rule = NULL;
4899e52c53bSPaolo Bonzini 
4909e52c53bSPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
4919e52c53bSPaolo Bonzini         if (rule->options.inject.sector == -1) {
4929e52c53bSPaolo Bonzini             break;
4939e52c53bSPaolo Bonzini         }
4949e52c53bSPaolo Bonzini     }
4959e52c53bSPaolo Bonzini 
4969e52c53bSPaolo Bonzini     if (rule && rule->options.inject.error) {
4979e52c53bSPaolo Bonzini         return inject_error(bs, cb, opaque, rule);
4989e52c53bSPaolo Bonzini     }
4999e52c53bSPaolo Bonzini 
5009a4f4c31SKevin Wolf     return bdrv_aio_flush(bs->file->bs, cb, opaque);
5019e52c53bSPaolo Bonzini }
5029e52c53bSPaolo Bonzini 
5033c90c65dSKevin Wolf 
5046a143727SKevin Wolf static void blkdebug_close(BlockDriverState *bs)
5056a143727SKevin Wolf {
5066a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5078b9b0cc2SKevin Wolf     BlkdebugRule *rule, *next;
5088b9b0cc2SKevin Wolf     int i;
5098b9b0cc2SKevin Wolf 
5107fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
5118b9b0cc2SKevin Wolf         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
5129e35542bSKevin Wolf             remove_rule(rule);
5138b9b0cc2SKevin Wolf         }
5148b9b0cc2SKevin Wolf     }
5156a143727SKevin Wolf }
5166a143727SKevin Wolf 
5173c90c65dSKevin Wolf static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
5183c90c65dSKevin Wolf {
5193c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5203c90c65dSKevin Wolf     BlkdebugSuspendedReq r;
5213c90c65dSKevin Wolf 
5223c90c65dSKevin Wolf     r = (BlkdebugSuspendedReq) {
5233c90c65dSKevin Wolf         .co         = qemu_coroutine_self(),
5243c90c65dSKevin Wolf         .tag        = g_strdup(rule->options.suspend.tag),
5253c90c65dSKevin Wolf     };
5263c90c65dSKevin Wolf 
5273c90c65dSKevin Wolf     remove_rule(rule);
5283c90c65dSKevin Wolf     QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
5293c90c65dSKevin Wolf 
53020873526SMichael S. Tsirkin     if (!qtest_enabled()) {
5313c90c65dSKevin Wolf         printf("blkdebug: Suspended request '%s'\n", r.tag);
53220873526SMichael S. Tsirkin     }
5333c90c65dSKevin Wolf     qemu_coroutine_yield();
53420873526SMichael S. Tsirkin     if (!qtest_enabled()) {
5353c90c65dSKevin Wolf         printf("blkdebug: Resuming request '%s'\n", r.tag);
53620873526SMichael S. Tsirkin     }
5373c90c65dSKevin Wolf 
5383c90c65dSKevin Wolf     QLIST_REMOVE(&r, next);
5393c90c65dSKevin Wolf     g_free(r.tag);
5403c90c65dSKevin Wolf }
5413c90c65dSKevin Wolf 
542571cd43eSPaolo Bonzini static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
5438f96b5beSPaolo Bonzini     bool injected)
5448b9b0cc2SKevin Wolf {
5458b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5468b9b0cc2SKevin Wolf 
5478b9b0cc2SKevin Wolf     /* Only process rules for the current state */
5488f96b5beSPaolo Bonzini     if (rule->state && rule->state != s->state) {
549571cd43eSPaolo Bonzini         return injected;
5508b9b0cc2SKevin Wolf     }
5518b9b0cc2SKevin Wolf 
5528b9b0cc2SKevin Wolf     /* Take the action */
5538b9b0cc2SKevin Wolf     switch (rule->action) {
5548b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
555571cd43eSPaolo Bonzini         if (!injected) {
556571cd43eSPaolo Bonzini             QSIMPLEQ_INIT(&s->active_rules);
557571cd43eSPaolo Bonzini             injected = true;
558571cd43eSPaolo Bonzini         }
559571cd43eSPaolo Bonzini         QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
5608b9b0cc2SKevin Wolf         break;
5618b9b0cc2SKevin Wolf 
5628b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
5638f96b5beSPaolo Bonzini         s->new_state = rule->options.set_state.new_state;
5648b9b0cc2SKevin Wolf         break;
5653c90c65dSKevin Wolf 
5663c90c65dSKevin Wolf     case ACTION_SUSPEND:
5673c90c65dSKevin Wolf         suspend_request(bs, rule);
5683c90c65dSKevin Wolf         break;
5698b9b0cc2SKevin Wolf     }
570571cd43eSPaolo Bonzini     return injected;
5718b9b0cc2SKevin Wolf }
5728b9b0cc2SKevin Wolf 
573a31939e6SEric Blake static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
5748b9b0cc2SKevin Wolf {
5758b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5763c90c65dSKevin Wolf     struct BlkdebugRule *rule, *next;
577571cd43eSPaolo Bonzini     bool injected;
5788b9b0cc2SKevin Wolf 
5797fb1cf16SEric Blake     assert((int)event >= 0 && event < BLKDBG__MAX);
5808b9b0cc2SKevin Wolf 
581571cd43eSPaolo Bonzini     injected = false;
5828f96b5beSPaolo Bonzini     s->new_state = s->state;
5833c90c65dSKevin Wolf     QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
5848f96b5beSPaolo Bonzini         injected = process_rule(bs, rule, injected);
5858b9b0cc2SKevin Wolf     }
5868f96b5beSPaolo Bonzini     s->state = s->new_state;
5878b9b0cc2SKevin Wolf }
5888b9b0cc2SKevin Wolf 
5893c90c65dSKevin Wolf static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
5903c90c65dSKevin Wolf                                      const char *tag)
5913c90c65dSKevin Wolf {
5923c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5933c90c65dSKevin Wolf     struct BlkdebugRule *rule;
594a31939e6SEric Blake     BlkdebugEvent blkdebug_event;
5953c90c65dSKevin Wolf 
5963c90c65dSKevin Wolf     if (get_event_by_name(event, &blkdebug_event) < 0) {
5973c90c65dSKevin Wolf         return -ENOENT;
5983c90c65dSKevin Wolf     }
5993c90c65dSKevin Wolf 
6003c90c65dSKevin Wolf 
6013c90c65dSKevin Wolf     rule = g_malloc(sizeof(*rule));
6023c90c65dSKevin Wolf     *rule = (struct BlkdebugRule) {
6033c90c65dSKevin Wolf         .event  = blkdebug_event,
6043c90c65dSKevin Wolf         .action = ACTION_SUSPEND,
6053c90c65dSKevin Wolf         .state  = 0,
6063c90c65dSKevin Wolf         .options.suspend.tag = g_strdup(tag),
6073c90c65dSKevin Wolf     };
6083c90c65dSKevin Wolf 
6093c90c65dSKevin Wolf     QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
6103c90c65dSKevin Wolf 
6113c90c65dSKevin Wolf     return 0;
6123c90c65dSKevin Wolf }
6133c90c65dSKevin Wolf 
6143c90c65dSKevin Wolf static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
6153c90c65dSKevin Wolf {
6163c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
617c547e564SFam Zheng     BlkdebugSuspendedReq *r, *next;
6183c90c65dSKevin Wolf 
619c547e564SFam Zheng     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
6203c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
6213c90c65dSKevin Wolf             qemu_coroutine_enter(r->co, NULL);
6223c90c65dSKevin Wolf             return 0;
6233c90c65dSKevin Wolf         }
6243c90c65dSKevin Wolf     }
6253c90c65dSKevin Wolf     return -ENOENT;
6263c90c65dSKevin Wolf }
6273c90c65dSKevin Wolf 
6284cc70e93SFam Zheng static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
6294cc70e93SFam Zheng                                             const char *tag)
6304cc70e93SFam Zheng {
6314cc70e93SFam Zheng     BDRVBlkdebugState *s = bs->opaque;
632c547e564SFam Zheng     BlkdebugSuspendedReq *r, *r_next;
6334cc70e93SFam Zheng     BlkdebugRule *rule, *next;
6344cc70e93SFam Zheng     int i, ret = -ENOENT;
6354cc70e93SFam Zheng 
6367fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
6374cc70e93SFam Zheng         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
6384cc70e93SFam Zheng             if (rule->action == ACTION_SUSPEND &&
6394cc70e93SFam Zheng                 !strcmp(rule->options.suspend.tag, tag)) {
6404cc70e93SFam Zheng                 remove_rule(rule);
6414cc70e93SFam Zheng                 ret = 0;
6424cc70e93SFam Zheng             }
6434cc70e93SFam Zheng         }
6444cc70e93SFam Zheng     }
645c547e564SFam Zheng     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
6464cc70e93SFam Zheng         if (!strcmp(r->tag, tag)) {
6474cc70e93SFam Zheng             qemu_coroutine_enter(r->co, NULL);
6484cc70e93SFam Zheng             ret = 0;
6494cc70e93SFam Zheng         }
6504cc70e93SFam Zheng     }
6514cc70e93SFam Zheng     return ret;
6524cc70e93SFam Zheng }
6533c90c65dSKevin Wolf 
6543c90c65dSKevin Wolf static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
6553c90c65dSKevin Wolf {
6563c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
6573c90c65dSKevin Wolf     BlkdebugSuspendedReq *r;
6583c90c65dSKevin Wolf 
6593c90c65dSKevin Wolf     QLIST_FOREACH(r, &s->suspended_reqs, next) {
6603c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
6613c90c65dSKevin Wolf             return true;
6623c90c65dSKevin Wolf         }
6633c90c65dSKevin Wolf     }
6643c90c65dSKevin Wolf     return false;
6653c90c65dSKevin Wolf }
6663c90c65dSKevin Wolf 
667e1302255SPaolo Bonzini static int64_t blkdebug_getlength(BlockDriverState *bs)
668e1302255SPaolo Bonzini {
6699a4f4c31SKevin Wolf     return bdrv_getlength(bs->file->bs);
670e1302255SPaolo Bonzini }
671e1302255SPaolo Bonzini 
6728eedfbd4SKevin Wolf static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
6738eedfbd4SKevin Wolf {
6749a4f4c31SKevin Wolf     return bdrv_truncate(bs->file->bs, offset);
6758eedfbd4SKevin Wolf }
6768eedfbd4SKevin Wolf 
677*4cdd01d3SKevin Wolf static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
6782c31b04cSMax Reitz {
6792c31b04cSMax Reitz     QDict *opts;
6808779441bSMax Reitz     const QDictEntry *e;
6818779441bSMax Reitz     bool force_json = false;
6822c31b04cSMax Reitz 
683*4cdd01d3SKevin Wolf     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
6848779441bSMax Reitz         if (strcmp(qdict_entry_key(e), "config") &&
685*4cdd01d3SKevin Wolf             strcmp(qdict_entry_key(e), "x-image"))
6868779441bSMax Reitz         {
6878779441bSMax Reitz             force_json = true;
6888779441bSMax Reitz             break;
6898779441bSMax Reitz         }
6908779441bSMax Reitz     }
6918779441bSMax Reitz 
6929a4f4c31SKevin Wolf     if (force_json && !bs->file->bs->full_open_options) {
6932c31b04cSMax Reitz         /* The config file cannot be recreated, so creating a plain filename
6942c31b04cSMax Reitz          * is impossible */
6952c31b04cSMax Reitz         return;
6962c31b04cSMax Reitz     }
6972c31b04cSMax Reitz 
6989a4f4c31SKevin Wolf     if (!force_json && bs->file->bs->exact_filename[0]) {
6998779441bSMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
7008779441bSMax Reitz                  "blkdebug:%s:%s",
701*4cdd01d3SKevin Wolf                  qdict_get_try_str(options, "config") ?: "",
7029a4f4c31SKevin Wolf                  bs->file->bs->exact_filename);
7038779441bSMax Reitz     }
7048779441bSMax Reitz 
7052c31b04cSMax Reitz     opts = qdict_new();
7062c31b04cSMax Reitz     qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug")));
7072c31b04cSMax Reitz 
7089a4f4c31SKevin Wolf     QINCREF(bs->file->bs->full_open_options);
7099a4f4c31SKevin Wolf     qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
7102c31b04cSMax Reitz 
711*4cdd01d3SKevin Wolf     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
712*4cdd01d3SKevin Wolf         if (strcmp(qdict_entry_key(e), "x-image")) {
7138779441bSMax Reitz             qobject_incref(qdict_entry_value(e));
7148779441bSMax Reitz             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
7152c31b04cSMax Reitz         }
7162c31b04cSMax Reitz     }
7172c31b04cSMax Reitz 
7182c31b04cSMax Reitz     bs->full_open_options = opts;
7192c31b04cSMax Reitz }
7202c31b04cSMax Reitz 
7216a143727SKevin Wolf static BlockDriver bdrv_blkdebug = {
7226a143727SKevin Wolf     .format_name            = "blkdebug",
7236a143727SKevin Wolf     .protocol_name          = "blkdebug",
7246a143727SKevin Wolf     .instance_size          = sizeof(BDRVBlkdebugState),
7256a143727SKevin Wolf 
726f4681212SKevin Wolf     .bdrv_parse_filename    = blkdebug_parse_filename,
72766f82ceeSKevin Wolf     .bdrv_file_open         = blkdebug_open,
7286a143727SKevin Wolf     .bdrv_close             = blkdebug_close,
729e1302255SPaolo Bonzini     .bdrv_getlength         = blkdebug_getlength,
7308eedfbd4SKevin Wolf     .bdrv_truncate          = blkdebug_truncate,
7312c31b04cSMax Reitz     .bdrv_refresh_filename  = blkdebug_refresh_filename,
7326a143727SKevin Wolf 
7336a143727SKevin Wolf     .bdrv_aio_readv         = blkdebug_aio_readv,
7346a143727SKevin Wolf     .bdrv_aio_writev        = blkdebug_aio_writev,
7359e52c53bSPaolo Bonzini     .bdrv_aio_flush         = blkdebug_aio_flush,
7368b9b0cc2SKevin Wolf 
7378b9b0cc2SKevin Wolf     .bdrv_debug_event           = blkdebug_debug_event,
7383c90c65dSKevin Wolf     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
7394cc70e93SFam Zheng     .bdrv_debug_remove_breakpoint
7404cc70e93SFam Zheng                                 = blkdebug_debug_remove_breakpoint,
7413c90c65dSKevin Wolf     .bdrv_debug_resume          = blkdebug_debug_resume,
7423c90c65dSKevin Wolf     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
7436a143727SKevin Wolf };
7446a143727SKevin Wolf 
7456a143727SKevin Wolf static void bdrv_blkdebug_init(void)
7466a143727SKevin Wolf {
7476a143727SKevin Wolf     bdrv_register(&bdrv_blkdebug);
7486a143727SKevin Wolf }
7496a143727SKevin Wolf 
7506a143727SKevin Wolf block_init(bdrv_blkdebug_init);
751