xref: /qemu/block/blkdebug.c (revision de6e7951)
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 
2580c71a24SPeter Maydell #include "qemu/osdep.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
27f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
281de7afc9SPaolo Bonzini #include "qemu/config-file.h"
29737e150eSPaolo Bonzini #include "block/block_int.h"
301de7afc9SPaolo Bonzini #include "qemu/module.h"
312c31b04cSMax Reitz #include "qapi/qmp/qbool.h"
322c31b04cSMax Reitz #include "qapi/qmp/qdict.h"
332c31b04cSMax Reitz #include "qapi/qmp/qint.h"
342c31b04cSMax Reitz #include "qapi/qmp/qstring.h"
3520873526SMichael S. Tsirkin #include "sysemu/qtest.h"
366a143727SKevin Wolf 
376a143727SKevin Wolf typedef struct BDRVBlkdebugState {
38571cd43eSPaolo Bonzini     int state;
398f96b5beSPaolo Bonzini     int new_state;
40835db3eeSEric Blake     int align;
413c90c65dSKevin Wolf 
42036990d7SMax Reitz     /* For blkdebug_refresh_filename() */
43036990d7SMax Reitz     char *config_file;
44036990d7SMax Reitz 
457fb1cf16SEric Blake     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
46571cd43eSPaolo Bonzini     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
473c90c65dSKevin Wolf     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
486a143727SKevin Wolf } BDRVBlkdebugState;
496a143727SKevin Wolf 
50b9f66d96SKevin Wolf typedef struct BlkdebugAIOCB {
517c84b1b8SMarkus Armbruster     BlockAIOCB common;
52b9f66d96SKevin Wolf     int ret;
53b9f66d96SKevin Wolf } BlkdebugAIOCB;
54b9f66d96SKevin Wolf 
553c90c65dSKevin Wolf typedef struct BlkdebugSuspendedReq {
563c90c65dSKevin Wolf     Coroutine *co;
573c90c65dSKevin Wolf     char *tag;
583c90c65dSKevin Wolf     QLIST_ENTRY(BlkdebugSuspendedReq) next;
593c90c65dSKevin Wolf } BlkdebugSuspendedReq;
603c90c65dSKevin Wolf 
618b9b0cc2SKevin Wolf enum {
628b9b0cc2SKevin Wolf     ACTION_INJECT_ERROR,
638b9b0cc2SKevin Wolf     ACTION_SET_STATE,
643c90c65dSKevin Wolf     ACTION_SUSPEND,
658b9b0cc2SKevin Wolf };
668b9b0cc2SKevin Wolf 
678b9b0cc2SKevin Wolf typedef struct BlkdebugRule {
68a31939e6SEric Blake     BlkdebugEvent event;
698b9b0cc2SKevin Wolf     int action;
708b9b0cc2SKevin Wolf     int state;
718b9b0cc2SKevin Wolf     union {
728b9b0cc2SKevin Wolf         struct {
738b9b0cc2SKevin Wolf             int error;
748b9b0cc2SKevin Wolf             int immediately;
758b9b0cc2SKevin Wolf             int once;
767c3a9985SKevin Wolf             int64_t offset;
778b9b0cc2SKevin Wolf         } inject;
788b9b0cc2SKevin Wolf         struct {
798b9b0cc2SKevin Wolf             int new_state;
808b9b0cc2SKevin Wolf         } set_state;
813c90c65dSKevin Wolf         struct {
823c90c65dSKevin Wolf             char *tag;
833c90c65dSKevin Wolf         } suspend;
848b9b0cc2SKevin Wolf     } options;
858b9b0cc2SKevin Wolf     QLIST_ENTRY(BlkdebugRule) next;
86571cd43eSPaolo Bonzini     QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
878b9b0cc2SKevin Wolf } BlkdebugRule;
888b9b0cc2SKevin Wolf 
898b9b0cc2SKevin Wolf static QemuOptsList inject_error_opts = {
908b9b0cc2SKevin Wolf     .name = "inject-error",
918b9b0cc2SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
928b9b0cc2SKevin Wolf     .desc = {
938b9b0cc2SKevin Wolf         {
948b9b0cc2SKevin Wolf             .name = "event",
958b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
968b9b0cc2SKevin Wolf         },
978b9b0cc2SKevin Wolf         {
988b9b0cc2SKevin Wolf             .name = "state",
998b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1008b9b0cc2SKevin Wolf         },
1018b9b0cc2SKevin Wolf         {
1028b9b0cc2SKevin Wolf             .name = "errno",
1038b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1048b9b0cc2SKevin Wolf         },
1058b9b0cc2SKevin Wolf         {
106e4780db4SPaolo Bonzini             .name = "sector",
107e4780db4SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
108e4780db4SPaolo Bonzini         },
109e4780db4SPaolo Bonzini         {
1108b9b0cc2SKevin Wolf             .name = "once",
1118b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1128b9b0cc2SKevin Wolf         },
1138b9b0cc2SKevin Wolf         {
1148b9b0cc2SKevin Wolf             .name = "immediately",
1158b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1168b9b0cc2SKevin Wolf         },
1178b9b0cc2SKevin Wolf         { /* end of list */ }
1188b9b0cc2SKevin Wolf     },
1198b9b0cc2SKevin Wolf };
1208b9b0cc2SKevin Wolf 
1218b9b0cc2SKevin Wolf static QemuOptsList set_state_opts = {
1228b9b0cc2SKevin Wolf     .name = "set-state",
123327cdad4SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
1248b9b0cc2SKevin Wolf     .desc = {
1258b9b0cc2SKevin Wolf         {
1268b9b0cc2SKevin Wolf             .name = "event",
1278b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
1288b9b0cc2SKevin Wolf         },
1298b9b0cc2SKevin Wolf         {
1308b9b0cc2SKevin Wolf             .name = "state",
1318b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1328b9b0cc2SKevin Wolf         },
1338b9b0cc2SKevin Wolf         {
1348b9b0cc2SKevin Wolf             .name = "new_state",
1358b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1368b9b0cc2SKevin Wolf         },
1378b9b0cc2SKevin Wolf         { /* end of list */ }
1388b9b0cc2SKevin Wolf     },
1398b9b0cc2SKevin Wolf };
1408b9b0cc2SKevin Wolf 
1418b9b0cc2SKevin Wolf static QemuOptsList *config_groups[] = {
1428b9b0cc2SKevin Wolf     &inject_error_opts,
1438b9b0cc2SKevin Wolf     &set_state_opts,
1448b9b0cc2SKevin Wolf     NULL
1458b9b0cc2SKevin Wolf };
1468b9b0cc2SKevin Wolf 
147a31939e6SEric Blake static int get_event_by_name(const char *name, BlkdebugEvent *event)
1488b9b0cc2SKevin Wolf {
1498b9b0cc2SKevin Wolf     int i;
1508b9b0cc2SKevin Wolf 
1517fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
152a31939e6SEric Blake         if (!strcmp(BlkdebugEvent_lookup[i], name)) {
1538b9b0cc2SKevin Wolf             *event = i;
1548b9b0cc2SKevin Wolf             return 0;
1558b9b0cc2SKevin Wolf         }
1568b9b0cc2SKevin Wolf     }
1578b9b0cc2SKevin Wolf 
1588b9b0cc2SKevin Wolf     return -1;
1598b9b0cc2SKevin Wolf }
1608b9b0cc2SKevin Wolf 
1618b9b0cc2SKevin Wolf struct add_rule_data {
1628b9b0cc2SKevin Wolf     BDRVBlkdebugState *s;
1638b9b0cc2SKevin Wolf     int action;
1648b9b0cc2SKevin Wolf };
1658b9b0cc2SKevin Wolf 
16628d0de7aSMarkus Armbruster static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
1678b9b0cc2SKevin Wolf {
1688b9b0cc2SKevin Wolf     struct add_rule_data *d = opaque;
1698b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = d->s;
1708b9b0cc2SKevin Wolf     const char* event_name;
171a31939e6SEric Blake     BlkdebugEvent event;
1728b9b0cc2SKevin Wolf     struct BlkdebugRule *rule;
1737c3a9985SKevin Wolf     int64_t sector;
1748b9b0cc2SKevin Wolf 
1758b9b0cc2SKevin Wolf     /* Find the right event for the rule */
1768b9b0cc2SKevin Wolf     event_name = qemu_opt_get(opts, "event");
177d4362d64SStefan Hajnoczi     if (!event_name) {
1788809cfc3SMarkus Armbruster         error_setg(errp, "Missing event name for rule");
179d4362d64SStefan Hajnoczi         return -1;
180d4362d64SStefan Hajnoczi     } else if (get_event_by_name(event_name, &event) < 0) {
1818809cfc3SMarkus Armbruster         error_setg(errp, "Invalid event name \"%s\"", event_name);
1828b9b0cc2SKevin Wolf         return -1;
1838b9b0cc2SKevin Wolf     }
1848b9b0cc2SKevin Wolf 
1858b9b0cc2SKevin Wolf     /* Set attributes common for all actions */
1867267c094SAnthony Liguori     rule = g_malloc0(sizeof(*rule));
1878b9b0cc2SKevin Wolf     *rule = (struct BlkdebugRule) {
1888b9b0cc2SKevin Wolf         .event  = event,
1898b9b0cc2SKevin Wolf         .action = d->action,
1908b9b0cc2SKevin Wolf         .state  = qemu_opt_get_number(opts, "state", 0),
1918b9b0cc2SKevin Wolf     };
1928b9b0cc2SKevin Wolf 
1938b9b0cc2SKevin Wolf     /* Parse action-specific options */
1948b9b0cc2SKevin Wolf     switch (d->action) {
1958b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
1968b9b0cc2SKevin Wolf         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
1978b9b0cc2SKevin Wolf         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
1988b9b0cc2SKevin Wolf         rule->options.inject.immediately =
1998b9b0cc2SKevin Wolf             qemu_opt_get_bool(opts, "immediately", 0);
2007c3a9985SKevin Wolf         sector = qemu_opt_get_number(opts, "sector", -1);
2017c3a9985SKevin Wolf         rule->options.inject.offset =
2027c3a9985SKevin Wolf             sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
2038b9b0cc2SKevin Wolf         break;
2048b9b0cc2SKevin Wolf 
2058b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
2068b9b0cc2SKevin Wolf         rule->options.set_state.new_state =
2078b9b0cc2SKevin Wolf             qemu_opt_get_number(opts, "new_state", 0);
2088b9b0cc2SKevin Wolf         break;
2093c90c65dSKevin Wolf 
2103c90c65dSKevin Wolf     case ACTION_SUSPEND:
2113c90c65dSKevin Wolf         rule->options.suspend.tag =
2123c90c65dSKevin Wolf             g_strdup(qemu_opt_get(opts, "tag"));
2133c90c65dSKevin Wolf         break;
2148b9b0cc2SKevin Wolf     };
2158b9b0cc2SKevin Wolf 
2168b9b0cc2SKevin Wolf     /* Add the rule */
2178b9b0cc2SKevin Wolf     QLIST_INSERT_HEAD(&s->rules[event], rule, next);
2188b9b0cc2SKevin Wolf 
2198b9b0cc2SKevin Wolf     return 0;
2208b9b0cc2SKevin Wolf }
2218b9b0cc2SKevin Wolf 
2229e35542bSKevin Wolf static void remove_rule(BlkdebugRule *rule)
2239e35542bSKevin Wolf {
2249e35542bSKevin Wolf     switch (rule->action) {
2259e35542bSKevin Wolf     case ACTION_INJECT_ERROR:
2269e35542bSKevin Wolf     case ACTION_SET_STATE:
2279e35542bSKevin Wolf         break;
2283c90c65dSKevin Wolf     case ACTION_SUSPEND:
2293c90c65dSKevin Wolf         g_free(rule->options.suspend.tag);
2303c90c65dSKevin Wolf         break;
2319e35542bSKevin Wolf     }
2329e35542bSKevin Wolf 
2339e35542bSKevin Wolf     QLIST_REMOVE(rule, next);
2349e35542bSKevin Wolf     g_free(rule);
2359e35542bSKevin Wolf }
2369e35542bSKevin Wolf 
23789f2b21eSMax Reitz static int read_config(BDRVBlkdebugState *s, const char *filename,
23889f2b21eSMax Reitz                        QDict *options, Error **errp)
2398b9b0cc2SKevin Wolf {
24085a040e5SMax Reitz     FILE *f = NULL;
2418b9b0cc2SKevin Wolf     int ret;
2428b9b0cc2SKevin Wolf     struct add_rule_data d;
24389f2b21eSMax Reitz     Error *local_err = NULL;
2448b9b0cc2SKevin Wolf 
24585a040e5SMax Reitz     if (filename) {
2468b9b0cc2SKevin Wolf         f = fopen(filename, "r");
2478b9b0cc2SKevin Wolf         if (f == NULL) {
248466b49f2SMax Reitz             error_setg_errno(errp, errno, "Could not read blkdebug config file");
2498b9b0cc2SKevin Wolf             return -errno;
2508b9b0cc2SKevin Wolf         }
2518b9b0cc2SKevin Wolf 
2528b9b0cc2SKevin Wolf         ret = qemu_config_parse(f, config_groups, filename);
2538b9b0cc2SKevin Wolf         if (ret < 0) {
254466b49f2SMax Reitz             error_setg(errp, "Could not parse blkdebug config file");
255466b49f2SMax Reitz             ret = -EINVAL;
2568b9b0cc2SKevin Wolf             goto fail;
2578b9b0cc2SKevin Wolf         }
25885a040e5SMax Reitz     }
2598b9b0cc2SKevin Wolf 
26089f2b21eSMax Reitz     qemu_config_parse_qdict(options, config_groups, &local_err);
26184d18f06SMarkus Armbruster     if (local_err) {
26289f2b21eSMax Reitz         error_propagate(errp, local_err);
26389f2b21eSMax Reitz         ret = -EINVAL;
26489f2b21eSMax Reitz         goto fail;
26589f2b21eSMax Reitz     }
26689f2b21eSMax Reitz 
2678b9b0cc2SKevin Wolf     d.s = s;
2688b9b0cc2SKevin Wolf     d.action = ACTION_INJECT_ERROR;
2698809cfc3SMarkus Armbruster     qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
270d4362d64SStefan Hajnoczi     if (local_err) {
271d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
272d4362d64SStefan Hajnoczi         ret = -EINVAL;
273d4362d64SStefan Hajnoczi         goto fail;
274d4362d64SStefan Hajnoczi     }
2758b9b0cc2SKevin Wolf 
2768b9b0cc2SKevin Wolf     d.action = ACTION_SET_STATE;
2778809cfc3SMarkus Armbruster     qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
278d4362d64SStefan Hajnoczi     if (local_err) {
279d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
280d4362d64SStefan Hajnoczi         ret = -EINVAL;
281d4362d64SStefan Hajnoczi         goto fail;
282d4362d64SStefan Hajnoczi     }
2838b9b0cc2SKevin Wolf 
2848b9b0cc2SKevin Wolf     ret = 0;
2858b9b0cc2SKevin Wolf fail:
286698f0d52SKevin Wolf     qemu_opts_reset(&inject_error_opts);
287698f0d52SKevin Wolf     qemu_opts_reset(&set_state_opts);
28885a040e5SMax Reitz     if (f) {
2898b9b0cc2SKevin Wolf         fclose(f);
29085a040e5SMax Reitz     }
2918b9b0cc2SKevin Wolf     return ret;
2928b9b0cc2SKevin Wolf }
2938b9b0cc2SKevin Wolf 
2948b9b0cc2SKevin Wolf /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
295f4681212SKevin Wolf static void blkdebug_parse_filename(const char *filename, QDict *options,
296f4681212SKevin Wolf                                     Error **errp)
297f4681212SKevin Wolf {
298f4681212SKevin Wolf     const char *c;
299f4681212SKevin Wolf 
300f4681212SKevin Wolf     /* Parse the blkdebug: prefix */
301f4681212SKevin Wolf     if (!strstart(filename, "blkdebug:", &filename)) {
302d4881b9bSMax Reitz         /* There was no prefix; therefore, all options have to be already
303d4881b9bSMax Reitz            present in the QDict (except for the filename) */
304d4881b9bSMax Reitz         qdict_put(options, "x-image", qstring_from_str(filename));
305f4681212SKevin Wolf         return;
306f4681212SKevin Wolf     }
307f4681212SKevin Wolf 
308f4681212SKevin Wolf     /* Parse config file path */
309f4681212SKevin Wolf     c = strchr(filename, ':');
310f4681212SKevin Wolf     if (c == NULL) {
311f4681212SKevin Wolf         error_setg(errp, "blkdebug requires both config file and image path");
312f4681212SKevin Wolf         return;
313f4681212SKevin Wolf     }
314f4681212SKevin Wolf 
315f4681212SKevin Wolf     if (c != filename) {
316f4681212SKevin Wolf         QString *config_path;
317f4681212SKevin Wolf         config_path = qstring_from_substr(filename, 0, c - filename - 1);
318f4681212SKevin Wolf         qdict_put(options, "config", config_path);
319f4681212SKevin Wolf     }
320f4681212SKevin Wolf 
321f4681212SKevin Wolf     /* TODO Allow multi-level nesting and set file.filename here */
322f4681212SKevin Wolf     filename = c + 1;
323f4681212SKevin Wolf     qdict_put(options, "x-image", qstring_from_str(filename));
324f4681212SKevin Wolf }
325f4681212SKevin Wolf 
326f4681212SKevin Wolf static QemuOptsList runtime_opts = {
327f4681212SKevin Wolf     .name = "blkdebug",
328f4681212SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
329f4681212SKevin Wolf     .desc = {
330f4681212SKevin Wolf         {
331f4681212SKevin Wolf             .name = "config",
332f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
333f4681212SKevin Wolf             .help = "Path to the configuration file",
334f4681212SKevin Wolf         },
335f4681212SKevin Wolf         {
336f4681212SKevin Wolf             .name = "x-image",
337f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
338f4681212SKevin Wolf             .help = "[internal use only, will be removed]",
339f4681212SKevin Wolf         },
340b35ee7fbSKevin Wolf         {
341b35ee7fbSKevin Wolf             .name = "align",
342b35ee7fbSKevin Wolf             .type = QEMU_OPT_SIZE,
343b35ee7fbSKevin Wolf             .help = "Required alignment in bytes",
344b35ee7fbSKevin Wolf         },
345f4681212SKevin Wolf         { /* end of list */ }
346f4681212SKevin Wolf     },
347f4681212SKevin Wolf };
348f4681212SKevin Wolf 
349015a1036SMax Reitz static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
350015a1036SMax Reitz                          Error **errp)
3516a143727SKevin Wolf {
3526a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
353f4681212SKevin Wolf     QemuOpts *opts;
354f4681212SKevin Wolf     Error *local_err = NULL;
355b35ee7fbSKevin Wolf     uint64_t align;
3568b9b0cc2SKevin Wolf     int ret;
3576a143727SKevin Wolf 
35887ea75d5SPeter Crosthwaite     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
359f4681212SKevin Wolf     qemu_opts_absorb_qdict(opts, options, &local_err);
36084d18f06SMarkus Armbruster     if (local_err) {
36110ffa72fSMax Reitz         error_propagate(errp, local_err);
362f4681212SKevin Wolf         ret = -EINVAL;
363eaf944a4SKevin Wolf         goto out;
3646a143727SKevin Wolf     }
3656a143727SKevin Wolf 
36689f2b21eSMax Reitz     /* Read rules from config file or command line options */
367036990d7SMax Reitz     s->config_file = g_strdup(qemu_opt_get(opts, "config"));
368036990d7SMax Reitz     ret = read_config(s, s->config_file, options, errp);
369466b49f2SMax Reitz     if (ret) {
370eaf944a4SKevin Wolf         goto out;
3718b9b0cc2SKevin Wolf     }
3728b9b0cc2SKevin Wolf 
3738db520ceSKevin Wolf     /* Set initial state */
374571cd43eSPaolo Bonzini     s->state = 1;
3758db520ceSKevin Wolf 
3766b826af7SFam Zheng     /* Open the image file */
3779a4f4c31SKevin Wolf     bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
378f3930ed0SKevin Wolf                                bs, &child_file, false, &local_err);
3799a4f4c31SKevin Wolf     if (local_err) {
3809a4f4c31SKevin Wolf         ret = -EINVAL;
38110ffa72fSMax Reitz         error_propagate(errp, local_err);
382eaf944a4SKevin Wolf         goto out;
383f4681212SKevin Wolf     }
384f4681212SKevin Wolf 
385b35ee7fbSKevin Wolf     /* Set request alignment */
386835db3eeSEric Blake     align = qemu_opt_get_size(opts, "align", 0);
387835db3eeSEric Blake     if (align < INT_MAX && is_power_of_2(align)) {
388835db3eeSEric Blake         s->align = align;
389835db3eeSEric Blake     } else if (align) {
390b35ee7fbSKevin Wolf         error_setg(errp, "Invalid alignment");
391b35ee7fbSKevin Wolf         ret = -EINVAL;
392de234897SMax Reitz         goto out;
393b35ee7fbSKevin Wolf     }
394b35ee7fbSKevin Wolf 
395f4681212SKevin Wolf     ret = 0;
396eaf944a4SKevin Wolf     goto out;
397eaf944a4SKevin Wolf 
398eaf944a4SKevin Wolf out:
399036990d7SMax Reitz     if (ret < 0) {
400036990d7SMax Reitz         g_free(s->config_file);
401036990d7SMax Reitz     }
402f4681212SKevin Wolf     qemu_opts_del(opts);
403f4681212SKevin Wolf     return ret;
4046a143727SKevin Wolf }
4056a143727SKevin Wolf 
4067c3a9985SKevin Wolf static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
407b9f66d96SKevin Wolf {
408b9f66d96SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
409571cd43eSPaolo Bonzini     int error = rule->options.inject.error;
410a069e2f1SJohn Snow     bool immediately = rule->options.inject.immediately;
411b9f66d96SKevin Wolf 
412571cd43eSPaolo Bonzini     if (rule->options.inject.once) {
413a069e2f1SJohn Snow         QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
414a069e2f1SJohn Snow         remove_rule(rule);
415b9f66d96SKevin Wolf     }
416b9f66d96SKevin Wolf 
4177c3a9985SKevin Wolf     if (!immediately) {
418e5c67ab5SPaolo Bonzini         aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
4197c3a9985SKevin Wolf         qemu_coroutine_yield();
420b9f66d96SKevin Wolf     }
421b9f66d96SKevin Wolf 
4227c3a9985SKevin Wolf     return -error;
423b9f66d96SKevin Wolf }
424b9f66d96SKevin Wolf 
4257c3a9985SKevin Wolf static int coroutine_fn
4267c3a9985SKevin Wolf blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
4277c3a9985SKevin Wolf                    QEMUIOVector *qiov, int flags)
4286a143727SKevin Wolf {
4296a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
430e4780db4SPaolo Bonzini     BlkdebugRule *rule = NULL;
431e4780db4SPaolo Bonzini 
432e4780db4SPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
4337c3a9985SKevin Wolf         uint64_t inject_offset = rule->options.inject.offset;
4347c3a9985SKevin Wolf 
4357c3a9985SKevin Wolf         if (inject_offset == -1 ||
4367c3a9985SKevin Wolf             (inject_offset >= offset && inject_offset < offset + bytes))
4377c3a9985SKevin Wolf         {
438e4780db4SPaolo Bonzini             break;
439e4780db4SPaolo Bonzini         }
440e4780db4SPaolo Bonzini     }
441b9f66d96SKevin Wolf 
442571cd43eSPaolo Bonzini     if (rule && rule->options.inject.error) {
4437c3a9985SKevin Wolf         return inject_error(bs, rule);
444b9f66d96SKevin Wolf     }
445b9f66d96SKevin Wolf 
4467c3a9985SKevin Wolf     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
4476a143727SKevin Wolf }
4486a143727SKevin Wolf 
4497c3a9985SKevin Wolf static int coroutine_fn
4507c3a9985SKevin Wolf blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
4517c3a9985SKevin Wolf                     QEMUIOVector *qiov, int flags)
4526a143727SKevin Wolf {
4536a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
454e4780db4SPaolo Bonzini     BlkdebugRule *rule = NULL;
455e4780db4SPaolo Bonzini 
456e4780db4SPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
4577c3a9985SKevin Wolf         uint64_t inject_offset = rule->options.inject.offset;
4587c3a9985SKevin Wolf 
4597c3a9985SKevin Wolf         if (inject_offset == -1 ||
4607c3a9985SKevin Wolf             (inject_offset >= offset && inject_offset < offset + bytes))
4617c3a9985SKevin Wolf         {
462e4780db4SPaolo Bonzini             break;
463e4780db4SPaolo Bonzini         }
464e4780db4SPaolo Bonzini     }
465b9f66d96SKevin Wolf 
466571cd43eSPaolo Bonzini     if (rule && rule->options.inject.error) {
4677c3a9985SKevin Wolf         return inject_error(bs, rule);
468b9f66d96SKevin Wolf     }
469b9f66d96SKevin Wolf 
4707c3a9985SKevin Wolf     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
4716a143727SKevin Wolf }
4726a143727SKevin Wolf 
4737c3a9985SKevin Wolf static int blkdebug_co_flush(BlockDriverState *bs)
4749e52c53bSPaolo Bonzini {
4759e52c53bSPaolo Bonzini     BDRVBlkdebugState *s = bs->opaque;
4769e52c53bSPaolo Bonzini     BlkdebugRule *rule = NULL;
4779e52c53bSPaolo Bonzini 
4789e52c53bSPaolo Bonzini     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
4797c3a9985SKevin Wolf         if (rule->options.inject.offset == -1) {
4809e52c53bSPaolo Bonzini             break;
4819e52c53bSPaolo Bonzini         }
4829e52c53bSPaolo Bonzini     }
4839e52c53bSPaolo Bonzini 
4849e52c53bSPaolo Bonzini     if (rule && rule->options.inject.error) {
4857c3a9985SKevin Wolf         return inject_error(bs, rule);
4869e52c53bSPaolo Bonzini     }
4879e52c53bSPaolo Bonzini 
4887c3a9985SKevin Wolf     return bdrv_co_flush(bs->file->bs);
4899e52c53bSPaolo Bonzini }
4909e52c53bSPaolo Bonzini 
4913c90c65dSKevin Wolf 
4926a143727SKevin Wolf static void blkdebug_close(BlockDriverState *bs)
4936a143727SKevin Wolf {
4946a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
4958b9b0cc2SKevin Wolf     BlkdebugRule *rule, *next;
4968b9b0cc2SKevin Wolf     int i;
4978b9b0cc2SKevin Wolf 
4987fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
4998b9b0cc2SKevin Wolf         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
5009e35542bSKevin Wolf             remove_rule(rule);
5018b9b0cc2SKevin Wolf         }
5028b9b0cc2SKevin Wolf     }
503036990d7SMax Reitz 
504036990d7SMax Reitz     g_free(s->config_file);
5056a143727SKevin Wolf }
5066a143727SKevin Wolf 
5073c90c65dSKevin Wolf static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
5083c90c65dSKevin Wolf {
5093c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5103c90c65dSKevin Wolf     BlkdebugSuspendedReq r;
5113c90c65dSKevin Wolf 
5123c90c65dSKevin Wolf     r = (BlkdebugSuspendedReq) {
5133c90c65dSKevin Wolf         .co         = qemu_coroutine_self(),
5143c90c65dSKevin Wolf         .tag        = g_strdup(rule->options.suspend.tag),
5153c90c65dSKevin Wolf     };
5163c90c65dSKevin Wolf 
5173c90c65dSKevin Wolf     remove_rule(rule);
5183c90c65dSKevin Wolf     QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
5193c90c65dSKevin Wolf 
52020873526SMichael S. Tsirkin     if (!qtest_enabled()) {
5213c90c65dSKevin Wolf         printf("blkdebug: Suspended request '%s'\n", r.tag);
52220873526SMichael S. Tsirkin     }
5233c90c65dSKevin Wolf     qemu_coroutine_yield();
52420873526SMichael S. Tsirkin     if (!qtest_enabled()) {
5253c90c65dSKevin Wolf         printf("blkdebug: Resuming request '%s'\n", r.tag);
52620873526SMichael S. Tsirkin     }
5273c90c65dSKevin Wolf 
5283c90c65dSKevin Wolf     QLIST_REMOVE(&r, next);
5293c90c65dSKevin Wolf     g_free(r.tag);
5303c90c65dSKevin Wolf }
5313c90c65dSKevin Wolf 
532571cd43eSPaolo Bonzini static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
5338f96b5beSPaolo Bonzini     bool injected)
5348b9b0cc2SKevin Wolf {
5358b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5368b9b0cc2SKevin Wolf 
5378b9b0cc2SKevin Wolf     /* Only process rules for the current state */
5388f96b5beSPaolo Bonzini     if (rule->state && rule->state != s->state) {
539571cd43eSPaolo Bonzini         return injected;
5408b9b0cc2SKevin Wolf     }
5418b9b0cc2SKevin Wolf 
5428b9b0cc2SKevin Wolf     /* Take the action */
5438b9b0cc2SKevin Wolf     switch (rule->action) {
5448b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
545571cd43eSPaolo Bonzini         if (!injected) {
546571cd43eSPaolo Bonzini             QSIMPLEQ_INIT(&s->active_rules);
547571cd43eSPaolo Bonzini             injected = true;
548571cd43eSPaolo Bonzini         }
549571cd43eSPaolo Bonzini         QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
5508b9b0cc2SKevin Wolf         break;
5518b9b0cc2SKevin Wolf 
5528b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
5538f96b5beSPaolo Bonzini         s->new_state = rule->options.set_state.new_state;
5548b9b0cc2SKevin Wolf         break;
5553c90c65dSKevin Wolf 
5563c90c65dSKevin Wolf     case ACTION_SUSPEND:
5573c90c65dSKevin Wolf         suspend_request(bs, rule);
5583c90c65dSKevin Wolf         break;
5598b9b0cc2SKevin Wolf     }
560571cd43eSPaolo Bonzini     return injected;
5618b9b0cc2SKevin Wolf }
5628b9b0cc2SKevin Wolf 
563a31939e6SEric Blake static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
5648b9b0cc2SKevin Wolf {
5658b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5663c90c65dSKevin Wolf     struct BlkdebugRule *rule, *next;
567571cd43eSPaolo Bonzini     bool injected;
5688b9b0cc2SKevin Wolf 
5697fb1cf16SEric Blake     assert((int)event >= 0 && event < BLKDBG__MAX);
5708b9b0cc2SKevin Wolf 
571571cd43eSPaolo Bonzini     injected = false;
5728f96b5beSPaolo Bonzini     s->new_state = s->state;
5733c90c65dSKevin Wolf     QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
5748f96b5beSPaolo Bonzini         injected = process_rule(bs, rule, injected);
5758b9b0cc2SKevin Wolf     }
5768f96b5beSPaolo Bonzini     s->state = s->new_state;
5778b9b0cc2SKevin Wolf }
5788b9b0cc2SKevin Wolf 
5793c90c65dSKevin Wolf static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
5803c90c65dSKevin Wolf                                      const char *tag)
5813c90c65dSKevin Wolf {
5823c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
5833c90c65dSKevin Wolf     struct BlkdebugRule *rule;
584a31939e6SEric Blake     BlkdebugEvent blkdebug_event;
5853c90c65dSKevin Wolf 
5863c90c65dSKevin Wolf     if (get_event_by_name(event, &blkdebug_event) < 0) {
5873c90c65dSKevin Wolf         return -ENOENT;
5883c90c65dSKevin Wolf     }
5893c90c65dSKevin Wolf 
5903c90c65dSKevin Wolf 
5913c90c65dSKevin Wolf     rule = g_malloc(sizeof(*rule));
5923c90c65dSKevin Wolf     *rule = (struct BlkdebugRule) {
5933c90c65dSKevin Wolf         .event  = blkdebug_event,
5943c90c65dSKevin Wolf         .action = ACTION_SUSPEND,
5953c90c65dSKevin Wolf         .state  = 0,
5963c90c65dSKevin Wolf         .options.suspend.tag = g_strdup(tag),
5973c90c65dSKevin Wolf     };
5983c90c65dSKevin Wolf 
5993c90c65dSKevin Wolf     QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
6003c90c65dSKevin Wolf 
6013c90c65dSKevin Wolf     return 0;
6023c90c65dSKevin Wolf }
6033c90c65dSKevin Wolf 
6043c90c65dSKevin Wolf static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
6053c90c65dSKevin Wolf {
6063c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
607c547e564SFam Zheng     BlkdebugSuspendedReq *r, *next;
6083c90c65dSKevin Wolf 
609c547e564SFam Zheng     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
6103c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
6110b8b8753SPaolo Bonzini             qemu_coroutine_enter(r->co);
6123c90c65dSKevin Wolf             return 0;
6133c90c65dSKevin Wolf         }
6143c90c65dSKevin Wolf     }
6153c90c65dSKevin Wolf     return -ENOENT;
6163c90c65dSKevin Wolf }
6173c90c65dSKevin Wolf 
6184cc70e93SFam Zheng static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
6194cc70e93SFam Zheng                                             const char *tag)
6204cc70e93SFam Zheng {
6214cc70e93SFam Zheng     BDRVBlkdebugState *s = bs->opaque;
622c547e564SFam Zheng     BlkdebugSuspendedReq *r, *r_next;
6234cc70e93SFam Zheng     BlkdebugRule *rule, *next;
6244cc70e93SFam Zheng     int i, ret = -ENOENT;
6254cc70e93SFam Zheng 
6267fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
6274cc70e93SFam Zheng         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
6284cc70e93SFam Zheng             if (rule->action == ACTION_SUSPEND &&
6294cc70e93SFam Zheng                 !strcmp(rule->options.suspend.tag, tag)) {
6304cc70e93SFam Zheng                 remove_rule(rule);
6314cc70e93SFam Zheng                 ret = 0;
6324cc70e93SFam Zheng             }
6334cc70e93SFam Zheng         }
6344cc70e93SFam Zheng     }
635c547e564SFam Zheng     QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
6364cc70e93SFam Zheng         if (!strcmp(r->tag, tag)) {
6370b8b8753SPaolo Bonzini             qemu_coroutine_enter(r->co);
6384cc70e93SFam Zheng             ret = 0;
6394cc70e93SFam Zheng         }
6404cc70e93SFam Zheng     }
6414cc70e93SFam Zheng     return ret;
6424cc70e93SFam Zheng }
6433c90c65dSKevin Wolf 
6443c90c65dSKevin Wolf static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
6453c90c65dSKevin Wolf {
6463c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
6473c90c65dSKevin Wolf     BlkdebugSuspendedReq *r;
6483c90c65dSKevin Wolf 
6493c90c65dSKevin Wolf     QLIST_FOREACH(r, &s->suspended_reqs, next) {
6503c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
6513c90c65dSKevin Wolf             return true;
6523c90c65dSKevin Wolf         }
6533c90c65dSKevin Wolf     }
6543c90c65dSKevin Wolf     return false;
6553c90c65dSKevin Wolf }
6563c90c65dSKevin Wolf 
657e1302255SPaolo Bonzini static int64_t blkdebug_getlength(BlockDriverState *bs)
658e1302255SPaolo Bonzini {
6599a4f4c31SKevin Wolf     return bdrv_getlength(bs->file->bs);
660e1302255SPaolo Bonzini }
661e1302255SPaolo Bonzini 
6624bff28b8SMax Reitz static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
6638eedfbd4SKevin Wolf {
6644bff28b8SMax Reitz     return bdrv_truncate(bs->file, offset, errp);
6658eedfbd4SKevin Wolf }
6668eedfbd4SKevin Wolf 
6674cdd01d3SKevin Wolf static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
6682c31b04cSMax Reitz {
669036990d7SMax Reitz     BDRVBlkdebugState *s = bs->opaque;
6702c31b04cSMax Reitz     QDict *opts;
6718779441bSMax Reitz     const QDictEntry *e;
6728779441bSMax Reitz     bool force_json = false;
6732c31b04cSMax Reitz 
6744cdd01d3SKevin Wolf     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
6758779441bSMax Reitz         if (strcmp(qdict_entry_key(e), "config") &&
6764cdd01d3SKevin Wolf             strcmp(qdict_entry_key(e), "x-image"))
6778779441bSMax Reitz         {
6788779441bSMax Reitz             force_json = true;
6798779441bSMax Reitz             break;
6808779441bSMax Reitz         }
6818779441bSMax Reitz     }
6828779441bSMax Reitz 
6839a4f4c31SKevin Wolf     if (force_json && !bs->file->bs->full_open_options) {
6842c31b04cSMax Reitz         /* The config file cannot be recreated, so creating a plain filename
6852c31b04cSMax Reitz          * is impossible */
6862c31b04cSMax Reitz         return;
6872c31b04cSMax Reitz     }
6882c31b04cSMax Reitz 
6899a4f4c31SKevin Wolf     if (!force_json && bs->file->bs->exact_filename[0]) {
6908779441bSMax Reitz         snprintf(bs->exact_filename, sizeof(bs->exact_filename),
691036990d7SMax Reitz                  "blkdebug:%s:%s", s->config_file ?: "",
6929a4f4c31SKevin Wolf                  bs->file->bs->exact_filename);
6938779441bSMax Reitz     }
6948779441bSMax Reitz 
6952c31b04cSMax Reitz     opts = qdict_new();
696*de6e7951SEric Blake     qdict_put(opts, "driver", qstring_from_str("blkdebug"));
6972c31b04cSMax Reitz 
6989a4f4c31SKevin Wolf     QINCREF(bs->file->bs->full_open_options);
699*de6e7951SEric Blake     qdict_put(opts, "image", bs->file->bs->full_open_options);
7002c31b04cSMax Reitz 
7014cdd01d3SKevin Wolf     for (e = qdict_first(options); e; e = qdict_next(options, e)) {
7024cdd01d3SKevin Wolf         if (strcmp(qdict_entry_key(e), "x-image")) {
7038779441bSMax Reitz             qobject_incref(qdict_entry_value(e));
7048779441bSMax Reitz             qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
7052c31b04cSMax Reitz         }
7062c31b04cSMax Reitz     }
7072c31b04cSMax Reitz 
7082c31b04cSMax Reitz     bs->full_open_options = opts;
7092c31b04cSMax Reitz }
7102c31b04cSMax Reitz 
711835db3eeSEric Blake static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
712835db3eeSEric Blake {
713835db3eeSEric Blake     BDRVBlkdebugState *s = bs->opaque;
714835db3eeSEric Blake 
715835db3eeSEric Blake     if (s->align) {
716a5b8dd2cSEric Blake         bs->bl.request_alignment = s->align;
717835db3eeSEric Blake     }
718835db3eeSEric Blake }
719835db3eeSEric Blake 
720c5e8bfb7SKevin Wolf static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
721c5e8bfb7SKevin Wolf                                    BlockReopenQueue *queue, Error **errp)
722c5e8bfb7SKevin Wolf {
723c5e8bfb7SKevin Wolf     return 0;
724c5e8bfb7SKevin Wolf }
725c5e8bfb7SKevin Wolf 
7266a143727SKevin Wolf static BlockDriver bdrv_blkdebug = {
7276a143727SKevin Wolf     .format_name            = "blkdebug",
7286a143727SKevin Wolf     .protocol_name          = "blkdebug",
7296a143727SKevin Wolf     .instance_size          = sizeof(BDRVBlkdebugState),
7306a143727SKevin Wolf 
731f4681212SKevin Wolf     .bdrv_parse_filename    = blkdebug_parse_filename,
73266f82ceeSKevin Wolf     .bdrv_file_open         = blkdebug_open,
7336a143727SKevin Wolf     .bdrv_close             = blkdebug_close,
734c5e8bfb7SKevin Wolf     .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
735d7010dfbSKevin Wolf     .bdrv_child_perm        = bdrv_filter_default_perms,
736d7010dfbSKevin Wolf 
737e1302255SPaolo Bonzini     .bdrv_getlength         = blkdebug_getlength,
7388eedfbd4SKevin Wolf     .bdrv_truncate          = blkdebug_truncate,
7392c31b04cSMax Reitz     .bdrv_refresh_filename  = blkdebug_refresh_filename,
740835db3eeSEric Blake     .bdrv_refresh_limits    = blkdebug_refresh_limits,
7416a143727SKevin Wolf 
7427c3a9985SKevin Wolf     .bdrv_co_preadv         = blkdebug_co_preadv,
7437c3a9985SKevin Wolf     .bdrv_co_pwritev        = blkdebug_co_pwritev,
7447c3a9985SKevin Wolf     .bdrv_co_flush_to_disk  = blkdebug_co_flush,
7458b9b0cc2SKevin Wolf 
7468b9b0cc2SKevin Wolf     .bdrv_debug_event           = blkdebug_debug_event,
7473c90c65dSKevin Wolf     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
7484cc70e93SFam Zheng     .bdrv_debug_remove_breakpoint
7494cc70e93SFam Zheng                                 = blkdebug_debug_remove_breakpoint,
7503c90c65dSKevin Wolf     .bdrv_debug_resume          = blkdebug_debug_resume,
7513c90c65dSKevin Wolf     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
7526a143727SKevin Wolf };
7536a143727SKevin Wolf 
7546a143727SKevin Wolf static void bdrv_blkdebug_init(void)
7556a143727SKevin Wolf {
7566a143727SKevin Wolf     bdrv_register(&bdrv_blkdebug);
7576a143727SKevin Wolf }
7586a143727SKevin Wolf 
7596a143727SKevin Wolf block_init(bdrv_blkdebug_init);
760