xref: /qemu/block/blkdebug.c (revision a4b740db)
16a143727SKevin Wolf /*
26a143727SKevin Wolf  * Block protocol for I/O error injection
36a143727SKevin Wolf  *
463188c24SEric Blake  * Copyright (C) 2016-2017 Red Hat, Inc.
56a143727SKevin Wolf  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
66a143727SKevin Wolf  *
76a143727SKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
86a143727SKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
96a143727SKevin Wolf  * in the Software without restriction, including without limitation the rights
106a143727SKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
116a143727SKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
126a143727SKevin Wolf  * furnished to do so, subject to the following conditions:
136a143727SKevin Wolf  *
146a143727SKevin Wolf  * The above copyright notice and this permission notice shall be included in
156a143727SKevin Wolf  * all copies or substantial portions of the Software.
166a143727SKevin Wolf  *
176a143727SKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
186a143727SKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
196a143727SKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
206a143727SKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
216a143727SKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
226a143727SKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
236a143727SKevin Wolf  * THE SOFTWARE.
246a143727SKevin Wolf  */
256a143727SKevin Wolf 
2680c71a24SPeter Maydell #include "qemu/osdep.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
28f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
291de7afc9SPaolo Bonzini #include "qemu/config-file.h"
30e2c1c34fSMarkus Armbruster #include "block/block-io.h"
31737e150eSPaolo Bonzini #include "block/block_int.h"
3269c6449fSMax Reitz #include "block/qdict.h"
331de7afc9SPaolo Bonzini #include "qemu/module.h"
34922a01a0SMarkus Armbruster #include "qemu/option.h"
3569c6449fSMax Reitz #include "qapi/qapi-visit-block-core.h"
362c31b04cSMax Reitz #include "qapi/qmp/qdict.h"
3769c6449fSMax Reitz #include "qapi/qmp/qlist.h"
382c31b04cSMax Reitz #include "qapi/qmp/qstring.h"
3969c6449fSMax Reitz #include "qapi/qobject-input-visitor.h"
4020873526SMichael S. Tsirkin #include "sysemu/qtest.h"
416a143727SKevin Wolf 
4236109bffSEmanuele Giuseppe Esposito /* All APIs are thread-safe */
4336109bffSEmanuele Giuseppe Esposito 
446a143727SKevin Wolf typedef struct BDRVBlkdebugState {
4536109bffSEmanuele Giuseppe Esposito     /* IN: initialized in blkdebug_open() and never changed */
463dc834f8SEric Blake     uint64_t align;
47430b26a8SEric Blake     uint64_t max_transfer;
48430b26a8SEric Blake     uint64_t opt_write_zero;
49430b26a8SEric Blake     uint64_t max_write_zero;
50430b26a8SEric Blake     uint64_t opt_discard;
51430b26a8SEric Blake     uint64_t max_discard;
5236109bffSEmanuele Giuseppe Esposito     char *config_file; /* For blkdebug_refresh_filename() */
5336109bffSEmanuele Giuseppe Esposito     /* initialized in blkdebug_parse_perms() */
5469c6449fSMax Reitz     uint64_t take_child_perms;
5569c6449fSMax Reitz     uint64_t unshare_child_perms;
5669c6449fSMax Reitz 
5736109bffSEmanuele Giuseppe Esposito     /* State. Protected by lock */
5836109bffSEmanuele Giuseppe Esposito     int state;
597fb1cf16SEric Blake     QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
60571cd43eSPaolo Bonzini     QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
613c90c65dSKevin Wolf     QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
6236109bffSEmanuele Giuseppe Esposito     QemuMutex lock;
636a143727SKevin Wolf } BDRVBlkdebugState;
646a143727SKevin Wolf 
65b9f66d96SKevin Wolf typedef struct BlkdebugAIOCB {
667c84b1b8SMarkus Armbruster     BlockAIOCB common;
67b9f66d96SKevin Wolf     int ret;
68b9f66d96SKevin Wolf } BlkdebugAIOCB;
69b9f66d96SKevin Wolf 
703c90c65dSKevin Wolf typedef struct BlkdebugSuspendedReq {
7136109bffSEmanuele Giuseppe Esposito     /* IN: initialized in suspend_request() */
723c90c65dSKevin Wolf     Coroutine *co;
733c90c65dSKevin Wolf     char *tag;
7436109bffSEmanuele Giuseppe Esposito 
7536109bffSEmanuele Giuseppe Esposito     /* List entry protected BDRVBlkdebugState's lock */
763c90c65dSKevin Wolf     QLIST_ENTRY(BlkdebugSuspendedReq) next;
773c90c65dSKevin Wolf } BlkdebugSuspendedReq;
783c90c65dSKevin Wolf 
798b9b0cc2SKevin Wolf enum {
808b9b0cc2SKevin Wolf     ACTION_INJECT_ERROR,
818b9b0cc2SKevin Wolf     ACTION_SET_STATE,
823c90c65dSKevin Wolf     ACTION_SUSPEND,
8351a46368SEmanuele Giuseppe Esposito     ACTION__MAX,
848b9b0cc2SKevin Wolf };
858b9b0cc2SKevin Wolf 
868b9b0cc2SKevin Wolf typedef struct BlkdebugRule {
8736109bffSEmanuele Giuseppe Esposito     /* IN: initialized in add_rule() or blkdebug_debug_breakpoint() */
88a31939e6SEric Blake     BlkdebugEvent event;
898b9b0cc2SKevin Wolf     int action;
908b9b0cc2SKevin Wolf     int state;
918b9b0cc2SKevin Wolf     union {
928b9b0cc2SKevin Wolf         struct {
9316789db3SMax Reitz             uint64_t iotype_mask;
948b9b0cc2SKevin Wolf             int error;
958b9b0cc2SKevin Wolf             int immediately;
968b9b0cc2SKevin Wolf             int once;
977c3a9985SKevin Wolf             int64_t offset;
988b9b0cc2SKevin Wolf         } inject;
998b9b0cc2SKevin Wolf         struct {
1008b9b0cc2SKevin Wolf             int new_state;
1018b9b0cc2SKevin Wolf         } set_state;
1023c90c65dSKevin Wolf         struct {
1033c90c65dSKevin Wolf             char *tag;
1043c90c65dSKevin Wolf         } suspend;
1058b9b0cc2SKevin Wolf     } options;
10636109bffSEmanuele Giuseppe Esposito 
10736109bffSEmanuele Giuseppe Esposito     /* List entries protected BDRVBlkdebugState's lock */
1088b9b0cc2SKevin Wolf     QLIST_ENTRY(BlkdebugRule) next;
109571cd43eSPaolo Bonzini     QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
1108b9b0cc2SKevin Wolf } BlkdebugRule;
1118b9b0cc2SKevin Wolf 
11216789db3SMax Reitz QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
11316789db3SMax Reitz                    "BlkdebugIOType mask does not fit into an uint64_t");
11416789db3SMax Reitz 
1158b9b0cc2SKevin Wolf static QemuOptsList inject_error_opts = {
1168b9b0cc2SKevin Wolf     .name = "inject-error",
1178b9b0cc2SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
1188b9b0cc2SKevin Wolf     .desc = {
1198b9b0cc2SKevin Wolf         {
1208b9b0cc2SKevin Wolf             .name = "event",
1218b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
1228b9b0cc2SKevin Wolf         },
1238b9b0cc2SKevin Wolf         {
1248b9b0cc2SKevin Wolf             .name = "state",
1258b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1268b9b0cc2SKevin Wolf         },
1278b9b0cc2SKevin Wolf         {
12816789db3SMax Reitz             .name = "iotype",
12916789db3SMax Reitz             .type = QEMU_OPT_STRING,
13016789db3SMax Reitz         },
13116789db3SMax Reitz         {
1328b9b0cc2SKevin Wolf             .name = "errno",
1338b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1348b9b0cc2SKevin Wolf         },
1358b9b0cc2SKevin Wolf         {
136e4780db4SPaolo Bonzini             .name = "sector",
137e4780db4SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
138e4780db4SPaolo Bonzini         },
139e4780db4SPaolo Bonzini         {
1408b9b0cc2SKevin Wolf             .name = "once",
1418b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1428b9b0cc2SKevin Wolf         },
1438b9b0cc2SKevin Wolf         {
1448b9b0cc2SKevin Wolf             .name = "immediately",
1458b9b0cc2SKevin Wolf             .type = QEMU_OPT_BOOL,
1468b9b0cc2SKevin Wolf         },
1478b9b0cc2SKevin Wolf         { /* end of list */ }
1488b9b0cc2SKevin Wolf     },
1498b9b0cc2SKevin Wolf };
1508b9b0cc2SKevin Wolf 
1518b9b0cc2SKevin Wolf static QemuOptsList set_state_opts = {
1528b9b0cc2SKevin Wolf     .name = "set-state",
153327cdad4SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
1548b9b0cc2SKevin Wolf     .desc = {
1558b9b0cc2SKevin Wolf         {
1568b9b0cc2SKevin Wolf             .name = "event",
1578b9b0cc2SKevin Wolf             .type = QEMU_OPT_STRING,
1588b9b0cc2SKevin Wolf         },
1598b9b0cc2SKevin Wolf         {
1608b9b0cc2SKevin Wolf             .name = "state",
1618b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1628b9b0cc2SKevin Wolf         },
1638b9b0cc2SKevin Wolf         {
1648b9b0cc2SKevin Wolf             .name = "new_state",
1658b9b0cc2SKevin Wolf             .type = QEMU_OPT_NUMBER,
1668b9b0cc2SKevin Wolf         },
1678b9b0cc2SKevin Wolf         { /* end of list */ }
1688b9b0cc2SKevin Wolf     },
1698b9b0cc2SKevin Wolf };
1708b9b0cc2SKevin Wolf 
1718b9b0cc2SKevin Wolf static QemuOptsList *config_groups[] = {
1728b9b0cc2SKevin Wolf     &inject_error_opts,
1738b9b0cc2SKevin Wolf     &set_state_opts,
1748b9b0cc2SKevin Wolf     NULL
1758b9b0cc2SKevin Wolf };
1768b9b0cc2SKevin Wolf 
1778b9b0cc2SKevin Wolf struct add_rule_data {
1788b9b0cc2SKevin Wolf     BDRVBlkdebugState *s;
1798b9b0cc2SKevin Wolf     int action;
1808b9b0cc2SKevin Wolf };
1818b9b0cc2SKevin Wolf 
add_rule(void * opaque,QemuOpts * opts,Error ** errp)18228d0de7aSMarkus Armbruster static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
1838b9b0cc2SKevin Wolf {
1848b9b0cc2SKevin Wolf     struct add_rule_data *d = opaque;
1858b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = d->s;
1868b9b0cc2SKevin Wolf     const char *event_name;
187f9509d15SMarc-André Lureau     int event;
1888b9b0cc2SKevin Wolf     struct BlkdebugRule *rule;
1897c3a9985SKevin Wolf     int64_t sector;
19016789db3SMax Reitz     BlkdebugIOType iotype;
19116789db3SMax Reitz     Error *local_error = NULL;
1928b9b0cc2SKevin Wolf 
1938b9b0cc2SKevin Wolf     /* Find the right event for the rule */
1948b9b0cc2SKevin Wolf     event_name = qemu_opt_get(opts, "event");
195d4362d64SStefan Hajnoczi     if (!event_name) {
1968809cfc3SMarkus Armbruster         error_setg(errp, "Missing event name for rule");
197d4362d64SStefan Hajnoczi         return -1;
198f9509d15SMarc-André Lureau     }
199f7abe0ecSMarc-André Lureau     event = qapi_enum_parse(&BlkdebugEvent_lookup, event_name, -1, errp);
200f9509d15SMarc-André Lureau     if (event < 0) {
2018b9b0cc2SKevin Wolf         return -1;
2028b9b0cc2SKevin Wolf     }
2038b9b0cc2SKevin Wolf 
2048b9b0cc2SKevin Wolf     /* Set attributes common for all actions */
2057267c094SAnthony Liguori     rule = g_malloc0(sizeof(*rule));
2068b9b0cc2SKevin Wolf     *rule = (struct BlkdebugRule) {
2078b9b0cc2SKevin Wolf         .event  = event,
2088b9b0cc2SKevin Wolf         .action = d->action,
2098b9b0cc2SKevin Wolf         .state  = qemu_opt_get_number(opts, "state", 0),
2108b9b0cc2SKevin Wolf     };
2118b9b0cc2SKevin Wolf 
2128b9b0cc2SKevin Wolf     /* Parse action-specific options */
2138b9b0cc2SKevin Wolf     switch (d->action) {
2148b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
2158b9b0cc2SKevin Wolf         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
2168b9b0cc2SKevin Wolf         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0);
2178b9b0cc2SKevin Wolf         rule->options.inject.immediately =
2188b9b0cc2SKevin Wolf             qemu_opt_get_bool(opts, "immediately", 0);
2197c3a9985SKevin Wolf         sector = qemu_opt_get_number(opts, "sector", -1);
2207c3a9985SKevin Wolf         rule->options.inject.offset =
2217c3a9985SKevin Wolf             sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
22216789db3SMax Reitz 
22316789db3SMax Reitz         iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
22416789db3SMax Reitz                                  qemu_opt_get(opts, "iotype"),
22516789db3SMax Reitz                                  BLKDEBUG_IO_TYPE__MAX, &local_error);
22616789db3SMax Reitz         if (local_error) {
22716789db3SMax Reitz             error_propagate(errp, local_error);
2285b4c95d0SElena Afanasova             g_free(rule);
22916789db3SMax Reitz             return -1;
23016789db3SMax Reitz         }
23116789db3SMax Reitz         if (iotype != BLKDEBUG_IO_TYPE__MAX) {
23216789db3SMax Reitz             rule->options.inject.iotype_mask = (1ull << iotype);
23316789db3SMax Reitz         } else {
23416789db3SMax Reitz             /* Apply the default */
23516789db3SMax Reitz             rule->options.inject.iotype_mask =
23616789db3SMax Reitz                 (1ull << BLKDEBUG_IO_TYPE_READ)
23716789db3SMax Reitz                 | (1ull << BLKDEBUG_IO_TYPE_WRITE)
23816789db3SMax Reitz                 | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
23916789db3SMax Reitz                 | (1ull << BLKDEBUG_IO_TYPE_DISCARD)
24016789db3SMax Reitz                 | (1ull << BLKDEBUG_IO_TYPE_FLUSH);
24116789db3SMax Reitz         }
24216789db3SMax Reitz 
2438b9b0cc2SKevin Wolf         break;
2448b9b0cc2SKevin Wolf 
2458b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
2468b9b0cc2SKevin Wolf         rule->options.set_state.new_state =
2478b9b0cc2SKevin Wolf             qemu_opt_get_number(opts, "new_state", 0);
2488b9b0cc2SKevin Wolf         break;
2493c90c65dSKevin Wolf 
2503c90c65dSKevin Wolf     case ACTION_SUSPEND:
2513c90c65dSKevin Wolf         rule->options.suspend.tag =
2523c90c65dSKevin Wolf             g_strdup(qemu_opt_get(opts, "tag"));
2533c90c65dSKevin Wolf         break;
2548b9b0cc2SKevin Wolf     };
2558b9b0cc2SKevin Wolf 
2568b9b0cc2SKevin Wolf     /* Add the rule */
25736109bffSEmanuele Giuseppe Esposito     qemu_mutex_lock(&s->lock);
2588b9b0cc2SKevin Wolf     QLIST_INSERT_HEAD(&s->rules[event], rule, next);
25936109bffSEmanuele Giuseppe Esposito     qemu_mutex_unlock(&s->lock);
2608b9b0cc2SKevin Wolf 
2618b9b0cc2SKevin Wolf     return 0;
2628b9b0cc2SKevin Wolf }
2638b9b0cc2SKevin Wolf 
26436109bffSEmanuele Giuseppe Esposito /* Called with lock held or from .bdrv_close */
remove_rule(BlkdebugRule * rule)2659e35542bSKevin Wolf static void remove_rule(BlkdebugRule *rule)
2669e35542bSKevin Wolf {
2679e35542bSKevin Wolf     switch (rule->action) {
2689e35542bSKevin Wolf     case ACTION_INJECT_ERROR:
2699e35542bSKevin Wolf     case ACTION_SET_STATE:
2709e35542bSKevin Wolf         break;
2713c90c65dSKevin Wolf     case ACTION_SUSPEND:
2723c90c65dSKevin Wolf         g_free(rule->options.suspend.tag);
2733c90c65dSKevin Wolf         break;
2749e35542bSKevin Wolf     }
2759e35542bSKevin Wolf 
2769e35542bSKevin Wolf     QLIST_REMOVE(rule, next);
2779e35542bSKevin Wolf     g_free(rule);
2789e35542bSKevin Wolf }
2799e35542bSKevin Wolf 
read_config(BDRVBlkdebugState * s,const char * filename,QDict * options,Error ** errp)28089f2b21eSMax Reitz static int read_config(BDRVBlkdebugState *s, const char *filename,
28189f2b21eSMax Reitz                        QDict *options, Error **errp)
2828b9b0cc2SKevin Wolf {
28385a040e5SMax Reitz     FILE *f = NULL;
2848b9b0cc2SKevin Wolf     int ret;
2858b9b0cc2SKevin Wolf     struct add_rule_data d;
28689f2b21eSMax Reitz     Error *local_err = NULL;
2878b9b0cc2SKevin Wolf 
28885a040e5SMax Reitz     if (filename) {
2898b9b0cc2SKevin Wolf         f = fopen(filename, "r");
2908b9b0cc2SKevin Wolf         if (f == NULL) {
291466b49f2SMax Reitz             error_setg_errno(errp, errno, "Could not read blkdebug config file");
2928b9b0cc2SKevin Wolf             return -errno;
2938b9b0cc2SKevin Wolf         }
2948b9b0cc2SKevin Wolf 
295f7544edcSPaolo Bonzini         ret = qemu_config_parse(f, config_groups, filename, errp);
2968b9b0cc2SKevin Wolf         if (ret < 0) {
2978b9b0cc2SKevin Wolf             goto fail;
2988b9b0cc2SKevin Wolf         }
29985a040e5SMax Reitz     }
3008b9b0cc2SKevin Wolf 
301f766e6dcSMarkus Armbruster     if (!qemu_config_parse_qdict(options, config_groups, errp)) {
30289f2b21eSMax Reitz         ret = -EINVAL;
30389f2b21eSMax Reitz         goto fail;
30489f2b21eSMax Reitz     }
30589f2b21eSMax Reitz 
3068b9b0cc2SKevin Wolf     d.s = s;
3078b9b0cc2SKevin Wolf     d.action = ACTION_INJECT_ERROR;
3088809cfc3SMarkus Armbruster     qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
309d4362d64SStefan Hajnoczi     if (local_err) {
310d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
311d4362d64SStefan Hajnoczi         ret = -EINVAL;
312d4362d64SStefan Hajnoczi         goto fail;
313d4362d64SStefan Hajnoczi     }
3148b9b0cc2SKevin Wolf 
3158b9b0cc2SKevin Wolf     d.action = ACTION_SET_STATE;
3168809cfc3SMarkus Armbruster     qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
317d4362d64SStefan Hajnoczi     if (local_err) {
318d4362d64SStefan Hajnoczi         error_propagate(errp, local_err);
319d4362d64SStefan Hajnoczi         ret = -EINVAL;
320d4362d64SStefan Hajnoczi         goto fail;
321d4362d64SStefan Hajnoczi     }
3228b9b0cc2SKevin Wolf 
3238b9b0cc2SKevin Wolf     ret = 0;
3248b9b0cc2SKevin Wolf fail:
325698f0d52SKevin Wolf     qemu_opts_reset(&inject_error_opts);
326698f0d52SKevin Wolf     qemu_opts_reset(&set_state_opts);
32785a040e5SMax Reitz     if (f) {
3288b9b0cc2SKevin Wolf         fclose(f);
32985a040e5SMax Reitz     }
3308b9b0cc2SKevin Wolf     return ret;
3318b9b0cc2SKevin Wolf }
3328b9b0cc2SKevin Wolf 
3338b9b0cc2SKevin Wolf /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
blkdebug_parse_filename(const char * filename,QDict * options,Error ** errp)334f4681212SKevin Wolf static void blkdebug_parse_filename(const char *filename, QDict *options,
335f4681212SKevin Wolf                                     Error **errp)
336f4681212SKevin Wolf {
337f4681212SKevin Wolf     const char *c;
338f4681212SKevin Wolf 
339f4681212SKevin Wolf     /* Parse the blkdebug: prefix */
340f4681212SKevin Wolf     if (!strstart(filename, "blkdebug:", &filename)) {
341d4881b9bSMax Reitz         /* There was no prefix; therefore, all options have to be already
342d4881b9bSMax Reitz            present in the QDict (except for the filename) */
34346f5ac20SEric Blake         qdict_put_str(options, "x-image", filename);
344f4681212SKevin Wolf         return;
345f4681212SKevin Wolf     }
346f4681212SKevin Wolf 
347f4681212SKevin Wolf     /* Parse config file path */
348f4681212SKevin Wolf     c = strchr(filename, ':');
349f4681212SKevin Wolf     if (c == NULL) {
350f4681212SKevin Wolf         error_setg(errp, "blkdebug requires both config file and image path");
351f4681212SKevin Wolf         return;
352f4681212SKevin Wolf     }
353f4681212SKevin Wolf 
354f4681212SKevin Wolf     if (c != filename) {
355f4681212SKevin Wolf         QString *config_path;
356ba891d68SMarkus Armbruster         config_path = qstring_from_substr(filename, 0, c - filename);
357f4681212SKevin Wolf         qdict_put(options, "config", config_path);
358f4681212SKevin Wolf     }
359f4681212SKevin Wolf 
360f4681212SKevin Wolf     /* TODO Allow multi-level nesting and set file.filename here */
361f4681212SKevin Wolf     filename = c + 1;
36246f5ac20SEric Blake     qdict_put_str(options, "x-image", filename);
363f4681212SKevin Wolf }
364f4681212SKevin Wolf 
blkdebug_parse_perm_list(uint64_t * dest,QDict * options,const char * prefix,Error ** errp)36569c6449fSMax Reitz static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
36669c6449fSMax Reitz                                     const char *prefix, Error **errp)
36769c6449fSMax Reitz {
36869c6449fSMax Reitz     int ret = 0;
36969c6449fSMax Reitz     QDict *subqdict = NULL;
37069c6449fSMax Reitz     QObject *crumpled_subqdict = NULL;
37169c6449fSMax Reitz     Visitor *v = NULL;
37269c6449fSMax Reitz     BlockPermissionList *perm_list = NULL, *element;
37369c6449fSMax Reitz 
37469c6449fSMax Reitz     *dest = 0;
37569c6449fSMax Reitz 
37669c6449fSMax Reitz     qdict_extract_subqdict(options, &subqdict, prefix);
37769c6449fSMax Reitz     if (!qdict_size(subqdict)) {
37869c6449fSMax Reitz         goto out;
37969c6449fSMax Reitz     }
38069c6449fSMax Reitz 
38169c6449fSMax Reitz     crumpled_subqdict = qdict_crumple(subqdict, errp);
38269c6449fSMax Reitz     if (!crumpled_subqdict) {
38369c6449fSMax Reitz         ret = -EINVAL;
38469c6449fSMax Reitz         goto out;
38569c6449fSMax Reitz     }
38669c6449fSMax Reitz 
38769c6449fSMax Reitz     v = qobject_input_visitor_new(crumpled_subqdict);
388af175e85SMarkus Armbruster     if (!visit_type_BlockPermissionList(v, NULL, &perm_list, errp)) {
38969c6449fSMax Reitz         ret = -EINVAL;
39069c6449fSMax Reitz         goto out;
39169c6449fSMax Reitz     }
39269c6449fSMax Reitz 
39369c6449fSMax Reitz     for (element = perm_list; element; element = element->next) {
39469c6449fSMax Reitz         *dest |= bdrv_qapi_perm_to_blk_perm(element->value);
39569c6449fSMax Reitz     }
39669c6449fSMax Reitz 
39769c6449fSMax Reitz out:
39869c6449fSMax Reitz     qapi_free_BlockPermissionList(perm_list);
39969c6449fSMax Reitz     visit_free(v);
40069c6449fSMax Reitz     qobject_unref(subqdict);
40169c6449fSMax Reitz     qobject_unref(crumpled_subqdict);
40269c6449fSMax Reitz     return ret;
40369c6449fSMax Reitz }
40469c6449fSMax Reitz 
blkdebug_parse_perms(BDRVBlkdebugState * s,QDict * options,Error ** errp)40569c6449fSMax Reitz static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
40669c6449fSMax Reitz                                 Error **errp)
40769c6449fSMax Reitz {
40869c6449fSMax Reitz     int ret;
40969c6449fSMax Reitz 
41069c6449fSMax Reitz     ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
41169c6449fSMax Reitz                                    "take-child-perms.", errp);
41269c6449fSMax Reitz     if (ret < 0) {
41369c6449fSMax Reitz         return ret;
41469c6449fSMax Reitz     }
41569c6449fSMax Reitz 
41669c6449fSMax Reitz     ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
41769c6449fSMax Reitz                                    "unshare-child-perms.", errp);
41869c6449fSMax Reitz     if (ret < 0) {
41969c6449fSMax Reitz         return ret;
42069c6449fSMax Reitz     }
42169c6449fSMax Reitz 
42269c6449fSMax Reitz     return 0;
42369c6449fSMax Reitz }
42469c6449fSMax Reitz 
425f4681212SKevin Wolf static QemuOptsList runtime_opts = {
426f4681212SKevin Wolf     .name = "blkdebug",
427f4681212SKevin Wolf     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
428f4681212SKevin Wolf     .desc = {
429f4681212SKevin Wolf         {
430f4681212SKevin Wolf             .name = "config",
431f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
432f4681212SKevin Wolf             .help = "Path to the configuration file",
433f4681212SKevin Wolf         },
434f4681212SKevin Wolf         {
435f4681212SKevin Wolf             .name = "x-image",
436f4681212SKevin Wolf             .type = QEMU_OPT_STRING,
437f4681212SKevin Wolf             .help = "[internal use only, will be removed]",
438f4681212SKevin Wolf         },
439b35ee7fbSKevin Wolf         {
440b35ee7fbSKevin Wolf             .name = "align",
441b35ee7fbSKevin Wolf             .type = QEMU_OPT_SIZE,
442b35ee7fbSKevin Wolf             .help = "Required alignment in bytes",
443b35ee7fbSKevin Wolf         },
444430b26a8SEric Blake         {
445430b26a8SEric Blake             .name = "max-transfer",
446430b26a8SEric Blake             .type = QEMU_OPT_SIZE,
447430b26a8SEric Blake             .help = "Maximum transfer size in bytes",
448430b26a8SEric Blake         },
449430b26a8SEric Blake         {
450430b26a8SEric Blake             .name = "opt-write-zero",
451430b26a8SEric Blake             .type = QEMU_OPT_SIZE,
452430b26a8SEric Blake             .help = "Optimum write zero alignment in bytes",
453430b26a8SEric Blake         },
454430b26a8SEric Blake         {
455430b26a8SEric Blake             .name = "max-write-zero",
456430b26a8SEric Blake             .type = QEMU_OPT_SIZE,
457430b26a8SEric Blake             .help = "Maximum write zero size in bytes",
458430b26a8SEric Blake         },
459430b26a8SEric Blake         {
460430b26a8SEric Blake             .name = "opt-discard",
461430b26a8SEric Blake             .type = QEMU_OPT_SIZE,
462430b26a8SEric Blake             .help = "Optimum discard alignment in bytes",
463430b26a8SEric Blake         },
464430b26a8SEric Blake         {
465430b26a8SEric Blake             .name = "max-discard",
466430b26a8SEric Blake             .type = QEMU_OPT_SIZE,
467430b26a8SEric Blake             .help = "Maximum discard size in bytes",
468430b26a8SEric Blake         },
469f4681212SKevin Wolf         { /* end of list */ }
470f4681212SKevin Wolf     },
471f4681212SKevin Wolf };
472f4681212SKevin Wolf 
blkdebug_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)473015a1036SMax Reitz static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
474015a1036SMax Reitz                          Error **errp)
4756a143727SKevin Wolf {
4766a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
477f4681212SKevin Wolf     QemuOpts *opts;
4788b9b0cc2SKevin Wolf     int ret;
479430b26a8SEric Blake     uint64_t align;
4806a143727SKevin Wolf 
48136109bffSEmanuele Giuseppe Esposito     qemu_mutex_init(&s->lock);
48287ea75d5SPeter Crosthwaite     opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
483af175e85SMarkus Armbruster     if (!qemu_opts_absorb_qdict(opts, options, errp)) {
484f4681212SKevin Wolf         ret = -EINVAL;
485eaf944a4SKevin Wolf         goto out;
4866a143727SKevin Wolf     }
4876a143727SKevin Wolf 
48889f2b21eSMax Reitz     /* Read rules from config file or command line options */
489036990d7SMax Reitz     s->config_file = g_strdup(qemu_opt_get(opts, "config"));
490036990d7SMax Reitz     ret = read_config(s, s->config_file, options, errp);
491466b49f2SMax Reitz     if (ret) {
492eaf944a4SKevin Wolf         goto out;
4938b9b0cc2SKevin Wolf     }
4948b9b0cc2SKevin Wolf 
4958db520ceSKevin Wolf     /* Set initial state */
496571cd43eSPaolo Bonzini     s->state = 1;
4978db520ceSKevin Wolf 
49869c6449fSMax Reitz     /* Parse permissions modifiers before opening the image file */
49969c6449fSMax Reitz     ret = blkdebug_parse_perms(s, options, errp);
50069c6449fSMax Reitz     if (ret < 0) {
50169c6449fSMax Reitz         goto out;
50269c6449fSMax Reitz     }
50369c6449fSMax Reitz 
5046b826af7SFam Zheng     /* Open the image file */
50583930780SVladimir Sementsov-Ogievskiy     ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image",
50683930780SVladimir Sementsov-Ogievskiy                                bs, errp);
50783930780SVladimir Sementsov-Ogievskiy     if (ret < 0) {
508eaf944a4SKevin Wolf         goto out;
509f4681212SKevin Wolf     }
510f4681212SKevin Wolf 
511a4b740dbSKevin Wolf     bdrv_graph_rdlock_main_loop();
512a4b740dbSKevin Wolf 
513228345bfSMax Reitz     bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
514228345bfSMax Reitz         (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
515228345bfSMax Reitz     bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
51680f5c33fSKevin Wolf         ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
517228345bfSMax Reitz             bs->file->bs->supported_zero_flags);
5183dc834f8SEric Blake     ret = -EINVAL;
51963188c24SEric Blake 
520430b26a8SEric Blake     /* Set alignment overrides */
5213dc834f8SEric Blake     s->align = qemu_opt_get_size(opts, "align", 0);
5223dc834f8SEric Blake     if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) {
5233dc834f8SEric Blake         error_setg(errp, "Cannot meet constraints with align %" PRIu64,
5243dc834f8SEric Blake                    s->align);
525a4b740dbSKevin Wolf         goto out_rdlock;
526b35ee7fbSKevin Wolf     }
527430b26a8SEric Blake     align = MAX(s->align, bs->file->bs->bl.request_alignment);
528430b26a8SEric Blake 
529430b26a8SEric Blake     s->max_transfer = qemu_opt_get_size(opts, "max-transfer", 0);
530430b26a8SEric Blake     if (s->max_transfer &&
531430b26a8SEric Blake         (s->max_transfer >= INT_MAX ||
532430b26a8SEric Blake          !QEMU_IS_ALIGNED(s->max_transfer, align))) {
533430b26a8SEric Blake         error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64,
534430b26a8SEric Blake                    s->max_transfer);
535a4b740dbSKevin Wolf         goto out_rdlock;
536430b26a8SEric Blake     }
537430b26a8SEric Blake 
538430b26a8SEric Blake     s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero", 0);
539430b26a8SEric Blake     if (s->opt_write_zero &&
540430b26a8SEric Blake         (s->opt_write_zero >= INT_MAX ||
541430b26a8SEric Blake          !QEMU_IS_ALIGNED(s->opt_write_zero, align))) {
542430b26a8SEric Blake         error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64,
543430b26a8SEric Blake                    s->opt_write_zero);
544a4b740dbSKevin Wolf         goto out_rdlock;
545430b26a8SEric Blake     }
546430b26a8SEric Blake 
547430b26a8SEric Blake     s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero", 0);
548430b26a8SEric Blake     if (s->max_write_zero &&
549430b26a8SEric Blake         (s->max_write_zero >= INT_MAX ||
550430b26a8SEric Blake          !QEMU_IS_ALIGNED(s->max_write_zero,
551430b26a8SEric Blake                           MAX(s->opt_write_zero, align)))) {
552430b26a8SEric Blake         error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64,
553430b26a8SEric Blake                    s->max_write_zero);
554a4b740dbSKevin Wolf         goto out_rdlock;
555430b26a8SEric Blake     }
556430b26a8SEric Blake 
557430b26a8SEric Blake     s->opt_discard = qemu_opt_get_size(opts, "opt-discard", 0);
558430b26a8SEric Blake     if (s->opt_discard &&
559430b26a8SEric Blake         (s->opt_discard >= INT_MAX ||
560430b26a8SEric Blake          !QEMU_IS_ALIGNED(s->opt_discard, align))) {
561430b26a8SEric Blake         error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64,
562430b26a8SEric Blake                    s->opt_discard);
563a4b740dbSKevin Wolf         goto out_rdlock;
564430b26a8SEric Blake     }
565430b26a8SEric Blake 
566430b26a8SEric Blake     s->max_discard = qemu_opt_get_size(opts, "max-discard", 0);
567430b26a8SEric Blake     if (s->max_discard &&
568430b26a8SEric Blake         (s->max_discard >= INT_MAX ||
569430b26a8SEric Blake          !QEMU_IS_ALIGNED(s->max_discard,
570430b26a8SEric Blake                           MAX(s->opt_discard, align)))) {
571430b26a8SEric Blake         error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64,
572430b26a8SEric Blake                    s->max_discard);
573a4b740dbSKevin Wolf         goto out_rdlock;
574430b26a8SEric Blake     }
575b35ee7fbSKevin Wolf 
576f8cec157SMax Reitz     bdrv_debug_event(bs, BLKDBG_NONE);
577f8cec157SMax Reitz 
578f4681212SKevin Wolf     ret = 0;
579a4b740dbSKevin Wolf out_rdlock:
580a4b740dbSKevin Wolf     bdrv_graph_rdunlock_main_loop();
581eaf944a4SKevin Wolf out:
582036990d7SMax Reitz     if (ret < 0) {
58336109bffSEmanuele Giuseppe Esposito         qemu_mutex_destroy(&s->lock);
584036990d7SMax Reitz         g_free(s->config_file);
585036990d7SMax Reitz     }
586f4681212SKevin Wolf     qemu_opts_del(opts);
587f4681212SKevin Wolf     return ret;
5886a143727SKevin Wolf }
5896a143727SKevin Wolf 
rule_check(BlockDriverState * bs,uint64_t offset,uint64_t bytes,BlkdebugIOType iotype)5902f1fabdfSPaolo Bonzini static int coroutine_fn rule_check(BlockDriverState *bs, uint64_t offset,
5912f1fabdfSPaolo Bonzini                                    uint64_t bytes, BlkdebugIOType iotype)
592b9f66d96SKevin Wolf {
593b9f66d96SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
594d157ed5fSEric Blake     BlkdebugRule *rule = NULL;
595d157ed5fSEric Blake     int error;
596d157ed5fSEric Blake     bool immediately;
597d157ed5fSEric Blake 
59836109bffSEmanuele Giuseppe Esposito     qemu_mutex_lock(&s->lock);
599d157ed5fSEric Blake     QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
600d157ed5fSEric Blake         uint64_t inject_offset = rule->options.inject.offset;
601d157ed5fSEric Blake 
60216789db3SMax Reitz         if ((inject_offset == -1 ||
603d157ed5fSEric Blake              (bytes && inject_offset >= offset &&
60416789db3SMax Reitz               inject_offset < offset + bytes)) &&
60516789db3SMax Reitz             (rule->options.inject.iotype_mask & (1ull << iotype)))
606d157ed5fSEric Blake         {
607d157ed5fSEric Blake             break;
608d157ed5fSEric Blake         }
609d157ed5fSEric Blake     }
610d157ed5fSEric Blake 
611d157ed5fSEric Blake     if (!rule || !rule->options.inject.error) {
61236109bffSEmanuele Giuseppe Esposito         qemu_mutex_unlock(&s->lock);
613d157ed5fSEric Blake         return 0;
614d157ed5fSEric Blake     }
615d157ed5fSEric Blake 
616d157ed5fSEric Blake     immediately = rule->options.inject.immediately;
617d157ed5fSEric Blake     error = rule->options.inject.error;
618b9f66d96SKevin Wolf 
619571cd43eSPaolo Bonzini     if (rule->options.inject.once) {
620a069e2f1SJohn Snow         QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next);
621a069e2f1SJohn Snow         remove_rule(rule);
622b9f66d96SKevin Wolf     }
623b9f66d96SKevin Wolf 
62436109bffSEmanuele Giuseppe Esposito     qemu_mutex_unlock(&s->lock);
6257c3a9985SKevin Wolf     if (!immediately) {
626e5c67ab5SPaolo Bonzini         aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
6277c3a9985SKevin Wolf         qemu_coroutine_yield();
628b9f66d96SKevin Wolf     }
629b9f66d96SKevin Wolf 
6307c3a9985SKevin Wolf     return -error;
631b9f66d96SKevin Wolf }
632b9f66d96SKevin Wolf 
633b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)634f7ef38ddSVladimir Sementsov-Ogievskiy blkdebug_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
635f7ef38ddSVladimir Sementsov-Ogievskiy                    QEMUIOVector *qiov, BdrvRequestFlags flags)
6366a143727SKevin Wolf {
637d157ed5fSEric Blake     int err;
638e4780db4SPaolo Bonzini 
639e0ef4395SEric Blake     /* Sanity check block layer guarantees */
640e0ef4395SEric Blake     assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
641e0ef4395SEric Blake     assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
642e0ef4395SEric Blake     if (bs->bl.max_transfer) {
643e0ef4395SEric Blake         assert(bytes <= bs->bl.max_transfer);
644e0ef4395SEric Blake     }
645e0ef4395SEric Blake 
64616789db3SMax Reitz     err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
647d157ed5fSEric Blake     if (err) {
648d157ed5fSEric Blake         return err;
649b9f66d96SKevin Wolf     }
650b9f66d96SKevin Wolf 
6517c3a9985SKevin Wolf     return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
6526a143727SKevin Wolf }
6536a143727SKevin Wolf 
654b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)655e75abedaSVladimir Sementsov-Ogievskiy blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
656e75abedaSVladimir Sementsov-Ogievskiy                     QEMUIOVector *qiov, BdrvRequestFlags flags)
6576a143727SKevin Wolf {
658d157ed5fSEric Blake     int err;
659e4780db4SPaolo Bonzini 
660e0ef4395SEric Blake     /* Sanity check block layer guarantees */
661e0ef4395SEric Blake     assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
662e0ef4395SEric Blake     assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
663e0ef4395SEric Blake     if (bs->bl.max_transfer) {
664e0ef4395SEric Blake         assert(bytes <= bs->bl.max_transfer);
665e0ef4395SEric Blake     }
666e0ef4395SEric Blake 
66716789db3SMax Reitz     err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
668d157ed5fSEric Blake     if (err) {
669d157ed5fSEric Blake         return err;
670b9f66d96SKevin Wolf     }
671b9f66d96SKevin Wolf 
6727c3a9985SKevin Wolf     return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
6736a143727SKevin Wolf }
6746a143727SKevin Wolf 
blkdebug_co_flush(BlockDriverState * bs)67588095349SEmanuele Giuseppe Esposito static int GRAPH_RDLOCK coroutine_fn blkdebug_co_flush(BlockDriverState *bs)
6769e52c53bSPaolo Bonzini {
67716789db3SMax Reitz     int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
6789e52c53bSPaolo Bonzini 
679d157ed5fSEric Blake     if (err) {
680d157ed5fSEric Blake         return err;
6819e52c53bSPaolo Bonzini     }
6829e52c53bSPaolo Bonzini 
6837c3a9985SKevin Wolf     return bdrv_co_flush(bs->file->bs);
6849e52c53bSPaolo Bonzini }
6859e52c53bSPaolo Bonzini 
686abaf8b75SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_pwrite_zeroes(BlockDriverState * bs,int64_t offset,int64_t bytes,BdrvRequestFlags flags)687abaf8b75SKevin Wolf blkdebug_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes,
68863188c24SEric Blake                           BdrvRequestFlags flags)
68963188c24SEric Blake {
69063188c24SEric Blake     uint32_t align = MAX(bs->bl.request_alignment,
69163188c24SEric Blake                          bs->bl.pwrite_zeroes_alignment);
69263188c24SEric Blake     int err;
69363188c24SEric Blake 
69463188c24SEric Blake     /* Only pass through requests that are larger than requested
69563188c24SEric Blake      * preferred alignment (so that we test the fallback to writes on
69663188c24SEric Blake      * unaligned portions), and check that the block layer never hands
69763188c24SEric Blake      * us anything unaligned that crosses an alignment boundary.  */
698f5a5ca79SManos Pitsidianakis     if (bytes < align) {
69963188c24SEric Blake         assert(QEMU_IS_ALIGNED(offset, align) ||
700f5a5ca79SManos Pitsidianakis                QEMU_IS_ALIGNED(offset + bytes, align) ||
70163188c24SEric Blake                DIV_ROUND_UP(offset, align) ==
702f5a5ca79SManos Pitsidianakis                DIV_ROUND_UP(offset + bytes, align));
70363188c24SEric Blake         return -ENOTSUP;
70463188c24SEric Blake     }
70563188c24SEric Blake     assert(QEMU_IS_ALIGNED(offset, align));
706f5a5ca79SManos Pitsidianakis     assert(QEMU_IS_ALIGNED(bytes, align));
70763188c24SEric Blake     if (bs->bl.max_pwrite_zeroes) {
708f5a5ca79SManos Pitsidianakis         assert(bytes <= bs->bl.max_pwrite_zeroes);
70963188c24SEric Blake     }
71063188c24SEric Blake 
71116789db3SMax Reitz     err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
71263188c24SEric Blake     if (err) {
71363188c24SEric Blake         return err;
71463188c24SEric Blake     }
71563188c24SEric Blake 
716f5a5ca79SManos Pitsidianakis     return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
71763188c24SEric Blake }
71863188c24SEric Blake 
7199a5a1c62SEmanuele Giuseppe Esposito static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_pdiscard(BlockDriverState * bs,int64_t offset,int64_t bytes)7209a5a1c62SEmanuele Giuseppe Esposito blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
72163188c24SEric Blake {
72263188c24SEric Blake     uint32_t align = bs->bl.pdiscard_alignment;
72363188c24SEric Blake     int err;
72463188c24SEric Blake 
72563188c24SEric Blake     /* Only pass through requests that are larger than requested
72663188c24SEric Blake      * minimum alignment, and ensure that unaligned requests do not
72763188c24SEric Blake      * cross optimum discard boundaries. */
728f5a5ca79SManos Pitsidianakis     if (bytes < bs->bl.request_alignment) {
72963188c24SEric Blake         assert(QEMU_IS_ALIGNED(offset, align) ||
730f5a5ca79SManos Pitsidianakis                QEMU_IS_ALIGNED(offset + bytes, align) ||
73163188c24SEric Blake                DIV_ROUND_UP(offset, align) ==
732f5a5ca79SManos Pitsidianakis                DIV_ROUND_UP(offset + bytes, align));
73363188c24SEric Blake         return -ENOTSUP;
73463188c24SEric Blake     }
73563188c24SEric Blake     assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
736f5a5ca79SManos Pitsidianakis     assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
737f5a5ca79SManos Pitsidianakis     if (align && bytes >= align) {
73863188c24SEric Blake         assert(QEMU_IS_ALIGNED(offset, align));
739f5a5ca79SManos Pitsidianakis         assert(QEMU_IS_ALIGNED(bytes, align));
74063188c24SEric Blake     }
74163188c24SEric Blake     if (bs->bl.max_pdiscard) {
742f5a5ca79SManos Pitsidianakis         assert(bytes <= bs->bl.max_pdiscard);
74363188c24SEric Blake     }
74463188c24SEric Blake 
74516789db3SMax Reitz     err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
74663188c24SEric Blake     if (err) {
74763188c24SEric Blake         return err;
74863188c24SEric Blake     }
74963188c24SEric Blake 
7500b9fd3f4SFam Zheng     return bdrv_co_pdiscard(bs->file, offset, bytes);
75163188c24SEric Blake }
7523c90c65dSKevin Wolf 
75379a55866SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
blkdebug_co_block_status(BlockDriverState * bs,bool want_zero,int64_t offset,int64_t bytes,int64_t * pnum,int64_t * map,BlockDriverState ** file)75479a55866SKevin Wolf blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
75579a55866SKevin Wolf                          int64_t bytes, int64_t *pnum, int64_t *map,
756efa6e2edSEric Blake                          BlockDriverState **file)
757efa6e2edSEric Blake {
7581adb0b5eSMax Reitz     int err;
7591adb0b5eSMax Reitz 
7603e4d0e72SEric Blake     assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
7611adb0b5eSMax Reitz 
7621adb0b5eSMax Reitz     err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
7631adb0b5eSMax Reitz     if (err) {
7641adb0b5eSMax Reitz         return err;
7651adb0b5eSMax Reitz     }
7661adb0b5eSMax Reitz 
767549ec0d9SMax Reitz     assert(bs->file && bs->file->bs);
768549ec0d9SMax Reitz     *pnum = bytes;
769549ec0d9SMax Reitz     *map = offset;
770549ec0d9SMax Reitz     *file = bs->file->bs;
771549ec0d9SMax Reitz     return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
772efa6e2edSEric Blake }
773efa6e2edSEric Blake 
blkdebug_close(BlockDriverState * bs)7746a143727SKevin Wolf static void blkdebug_close(BlockDriverState *bs)
7756a143727SKevin Wolf {
7766a143727SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
7778b9b0cc2SKevin Wolf     BlkdebugRule *rule, *next;
7788b9b0cc2SKevin Wolf     int i;
7798b9b0cc2SKevin Wolf 
7807fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
7818b9b0cc2SKevin Wolf         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
7829e35542bSKevin Wolf             remove_rule(rule);
7838b9b0cc2SKevin Wolf         }
7848b9b0cc2SKevin Wolf     }
785036990d7SMax Reitz 
786036990d7SMax Reitz     g_free(s->config_file);
78736109bffSEmanuele Giuseppe Esposito     qemu_mutex_destroy(&s->lock);
7886a143727SKevin Wolf }
7896a143727SKevin Wolf 
79036109bffSEmanuele Giuseppe Esposito /* Called with lock held.  */
suspend_request(BlockDriverState * bs,BlkdebugRule * rule)7913c90c65dSKevin Wolf static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
7923c90c65dSKevin Wolf {
7933c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
794f48ff5afSEmanuele Giuseppe Esposito     BlkdebugSuspendedReq *r;
7953c90c65dSKevin Wolf 
796f48ff5afSEmanuele Giuseppe Esposito     r = g_new(BlkdebugSuspendedReq, 1);
797f48ff5afSEmanuele Giuseppe Esposito 
798f48ff5afSEmanuele Giuseppe Esposito     r->co         = qemu_coroutine_self();
799f48ff5afSEmanuele Giuseppe Esposito     r->tag        = g_strdup(rule->options.suspend.tag);
8003c90c65dSKevin Wolf 
8013c90c65dSKevin Wolf     remove_rule(rule);
802f48ff5afSEmanuele Giuseppe Esposito     QLIST_INSERT_HEAD(&s->suspended_reqs, r, next);
8033c90c65dSKevin Wolf 
80420873526SMichael S. Tsirkin     if (!qtest_enabled()) {
805f48ff5afSEmanuele Giuseppe Esposito         printf("blkdebug: Suspended request '%s'\n", r->tag);
80620873526SMichael S. Tsirkin     }
8073c90c65dSKevin Wolf }
8083c90c65dSKevin Wolf 
80936109bffSEmanuele Giuseppe Esposito /* Called with lock held.  */
process_rule(BlockDriverState * bs,struct BlkdebugRule * rule,int * action_count,int * new_state)81051a46368SEmanuele Giuseppe Esposito static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
8114153b553SEmanuele Giuseppe Esposito                          int *action_count, int *new_state)
8128b9b0cc2SKevin Wolf {
8138b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
8148b9b0cc2SKevin Wolf 
8158b9b0cc2SKevin Wolf     /* Only process rules for the current state */
8168f96b5beSPaolo Bonzini     if (rule->state && rule->state != s->state) {
81751a46368SEmanuele Giuseppe Esposito         return;
8188b9b0cc2SKevin Wolf     }
8198b9b0cc2SKevin Wolf 
8208b9b0cc2SKevin Wolf     /* Take the action */
82151a46368SEmanuele Giuseppe Esposito     action_count[rule->action]++;
8228b9b0cc2SKevin Wolf     switch (rule->action) {
8238b9b0cc2SKevin Wolf     case ACTION_INJECT_ERROR:
82451a46368SEmanuele Giuseppe Esposito         if (action_count[ACTION_INJECT_ERROR] == 1) {
825571cd43eSPaolo Bonzini             QSIMPLEQ_INIT(&s->active_rules);
826571cd43eSPaolo Bonzini         }
827571cd43eSPaolo Bonzini         QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
8288b9b0cc2SKevin Wolf         break;
8298b9b0cc2SKevin Wolf 
8308b9b0cc2SKevin Wolf     case ACTION_SET_STATE:
8314153b553SEmanuele Giuseppe Esposito         *new_state = rule->options.set_state.new_state;
8328b9b0cc2SKevin Wolf         break;
8333c90c65dSKevin Wolf 
8343c90c65dSKevin Wolf     case ACTION_SUSPEND:
8353c90c65dSKevin Wolf         suspend_request(bs, rule);
8363c90c65dSKevin Wolf         break;
8378b9b0cc2SKevin Wolf     }
8388b9b0cc2SKevin Wolf }
8398b9b0cc2SKevin Wolf 
840c834dc05SEmanuele Giuseppe Esposito static void coroutine_fn
blkdebug_co_debug_event(BlockDriverState * bs,BlkdebugEvent event)841c834dc05SEmanuele Giuseppe Esposito blkdebug_co_debug_event(BlockDriverState *bs, BlkdebugEvent event)
8428b9b0cc2SKevin Wolf {
8438b9b0cc2SKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
8443c90c65dSKevin Wolf     struct BlkdebugRule *rule, *next;
8454153b553SEmanuele Giuseppe Esposito     int new_state;
84651a46368SEmanuele Giuseppe Esposito     int actions_count[ACTION__MAX] = { 0 };
8478b9b0cc2SKevin Wolf 
8487fb1cf16SEric Blake     assert((int)event >= 0 && event < BLKDBG__MAX);
8498b9b0cc2SKevin Wolf 
85036109bffSEmanuele Giuseppe Esposito     WITH_QEMU_LOCK_GUARD(&s->lock) {
8514153b553SEmanuele Giuseppe Esposito         new_state = s->state;
8523c90c65dSKevin Wolf         QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
8534153b553SEmanuele Giuseppe Esposito             process_rule(bs, rule, actions_count, &new_state);
8548b9b0cc2SKevin Wolf         }
8554153b553SEmanuele Giuseppe Esposito         s->state = new_state;
85636109bffSEmanuele Giuseppe Esposito     }
8572196c341SEmanuele Giuseppe Esposito 
8582196c341SEmanuele Giuseppe Esposito     while (actions_count[ACTION_SUSPEND] > 0) {
8592196c341SEmanuele Giuseppe Esposito         qemu_coroutine_yield();
8602196c341SEmanuele Giuseppe Esposito         actions_count[ACTION_SUSPEND]--;
8612196c341SEmanuele Giuseppe Esposito     }
8628b9b0cc2SKevin Wolf }
8638b9b0cc2SKevin Wolf 
blkdebug_debug_breakpoint(BlockDriverState * bs,const char * event,const char * tag)8643c90c65dSKevin Wolf static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
8653c90c65dSKevin Wolf                                      const char *tag)
8663c90c65dSKevin Wolf {
8673c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
8683c90c65dSKevin Wolf     struct BlkdebugRule *rule;
869f9509d15SMarc-André Lureau     int blkdebug_event;
8703c90c65dSKevin Wolf 
871f7abe0ecSMarc-André Lureau     blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL);
872f9509d15SMarc-André Lureau     if (blkdebug_event < 0) {
8733c90c65dSKevin Wolf         return -ENOENT;
8743c90c65dSKevin Wolf     }
8753c90c65dSKevin Wolf 
8763c90c65dSKevin Wolf     rule = g_malloc(sizeof(*rule));
8773c90c65dSKevin Wolf     *rule = (struct BlkdebugRule) {
8783c90c65dSKevin Wolf         .event  = blkdebug_event,
8793c90c65dSKevin Wolf         .action = ACTION_SUSPEND,
8803c90c65dSKevin Wolf         .state  = 0,
8813c90c65dSKevin Wolf         .options.suspend.tag = g_strdup(tag),
8823c90c65dSKevin Wolf     };
8833c90c65dSKevin Wolf 
88436109bffSEmanuele Giuseppe Esposito     qemu_mutex_lock(&s->lock);
8853c90c65dSKevin Wolf     QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
88636109bffSEmanuele Giuseppe Esposito     qemu_mutex_unlock(&s->lock);
8873c90c65dSKevin Wolf 
8883c90c65dSKevin Wolf     return 0;
8893c90c65dSKevin Wolf }
8903c90c65dSKevin Wolf 
89136109bffSEmanuele Giuseppe Esposito /* Called with lock held. May temporarily release lock. */
resume_req_by_tag(BDRVBlkdebugState * s,const char * tag,bool all)89269d0690cSEmanuele Giuseppe Esposito static int resume_req_by_tag(BDRVBlkdebugState *s, const char *tag, bool all)
8933c90c65dSKevin Wolf {
89469d0690cSEmanuele Giuseppe Esposito     BlkdebugSuspendedReq *r;
8953c90c65dSKevin Wolf 
89669d0690cSEmanuele Giuseppe Esposito retry:
89769d0690cSEmanuele Giuseppe Esposito     /*
89869d0690cSEmanuele Giuseppe Esposito      * No need for _SAFE, since a different coroutine can remove another node
89969d0690cSEmanuele Giuseppe Esposito      * (not the current one) in this list, and when the current one is removed
90069d0690cSEmanuele Giuseppe Esposito      * the iteration starts back from beginning anyways.
90169d0690cSEmanuele Giuseppe Esposito      */
90269d0690cSEmanuele Giuseppe Esposito     QLIST_FOREACH(r, &s->suspended_reqs, next) {
9033c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
904f48ff5afSEmanuele Giuseppe Esposito             Coroutine *co = r->co;
905f48ff5afSEmanuele Giuseppe Esposito 
906f48ff5afSEmanuele Giuseppe Esposito             if (!qtest_enabled()) {
907f48ff5afSEmanuele Giuseppe Esposito                 printf("blkdebug: Resuming request '%s'\n", r->tag);
908f48ff5afSEmanuele Giuseppe Esposito             }
909f48ff5afSEmanuele Giuseppe Esposito 
91069d0690cSEmanuele Giuseppe Esposito             QLIST_REMOVE(r, next);
911f48ff5afSEmanuele Giuseppe Esposito             g_free(r->tag);
912f48ff5afSEmanuele Giuseppe Esposito             g_free(r);
913f48ff5afSEmanuele Giuseppe Esposito 
91436109bffSEmanuele Giuseppe Esposito             qemu_mutex_unlock(&s->lock);
915f48ff5afSEmanuele Giuseppe Esposito             qemu_coroutine_enter(co);
91636109bffSEmanuele Giuseppe Esposito             qemu_mutex_lock(&s->lock);
917f48ff5afSEmanuele Giuseppe Esposito 
91869d0690cSEmanuele Giuseppe Esposito             if (all) {
91969d0690cSEmanuele Giuseppe Esposito                 goto retry;
92069d0690cSEmanuele Giuseppe Esposito             }
9213c90c65dSKevin Wolf             return 0;
9223c90c65dSKevin Wolf         }
9233c90c65dSKevin Wolf     }
9243c90c65dSKevin Wolf     return -ENOENT;
9253c90c65dSKevin Wolf }
9263c90c65dSKevin Wolf 
blkdebug_debug_resume(BlockDriverState * bs,const char * tag)92769d0690cSEmanuele Giuseppe Esposito static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
92869d0690cSEmanuele Giuseppe Esposito {
92969d0690cSEmanuele Giuseppe Esposito     BDRVBlkdebugState *s = bs->opaque;
93036109bffSEmanuele Giuseppe Esposito     QEMU_LOCK_GUARD(&s->lock);
93169d0690cSEmanuele Giuseppe Esposito     return resume_req_by_tag(s, tag, false);
93269d0690cSEmanuele Giuseppe Esposito }
93369d0690cSEmanuele Giuseppe Esposito 
blkdebug_debug_remove_breakpoint(BlockDriverState * bs,const char * tag)9344cc70e93SFam Zheng static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
9354cc70e93SFam Zheng                                             const char *tag)
9364cc70e93SFam Zheng {
9374cc70e93SFam Zheng     BDRVBlkdebugState *s = bs->opaque;
9384cc70e93SFam Zheng     BlkdebugRule *rule, *next;
9394cc70e93SFam Zheng     int i, ret = -ENOENT;
9404cc70e93SFam Zheng 
94136109bffSEmanuele Giuseppe Esposito     QEMU_LOCK_GUARD(&s->lock);
9427fb1cf16SEric Blake     for (i = 0; i < BLKDBG__MAX; i++) {
9434cc70e93SFam Zheng         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
9444cc70e93SFam Zheng             if (rule->action == ACTION_SUSPEND &&
9454cc70e93SFam Zheng                 !strcmp(rule->options.suspend.tag, tag)) {
9464cc70e93SFam Zheng                 remove_rule(rule);
9474cc70e93SFam Zheng                 ret = 0;
9484cc70e93SFam Zheng             }
9494cc70e93SFam Zheng         }
9504cc70e93SFam Zheng     }
95169d0690cSEmanuele Giuseppe Esposito     if (resume_req_by_tag(s, tag, true) == 0) {
9524cc70e93SFam Zheng         ret = 0;
9534cc70e93SFam Zheng     }
9544cc70e93SFam Zheng     return ret;
9554cc70e93SFam Zheng }
9563c90c65dSKevin Wolf 
blkdebug_debug_is_suspended(BlockDriverState * bs,const char * tag)9573c90c65dSKevin Wolf static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
9583c90c65dSKevin Wolf {
9593c90c65dSKevin Wolf     BDRVBlkdebugState *s = bs->opaque;
9603c90c65dSKevin Wolf     BlkdebugSuspendedReq *r;
9613c90c65dSKevin Wolf 
96236109bffSEmanuele Giuseppe Esposito     QEMU_LOCK_GUARD(&s->lock);
9633c90c65dSKevin Wolf     QLIST_FOREACH(r, &s->suspended_reqs, next) {
9643c90c65dSKevin Wolf         if (!strcmp(r->tag, tag)) {
9653c90c65dSKevin Wolf             return true;
9663c90c65dSKevin Wolf         }
9673c90c65dSKevin Wolf     }
9683c90c65dSKevin Wolf     return false;
9693c90c65dSKevin Wolf }
9703c90c65dSKevin Wolf 
9718ab8140aSKevin Wolf static int64_t coroutine_fn GRAPH_RDLOCK
blkdebug_co_getlength(BlockDriverState * bs)9728ab8140aSKevin Wolf blkdebug_co_getlength(BlockDriverState *bs)
973e1302255SPaolo Bonzini {
974c86422c5SEmanuele Giuseppe Esposito     return bdrv_co_getlength(bs->file->bs);
975e1302255SPaolo Bonzini }
976e1302255SPaolo Bonzini 
blkdebug_refresh_filename(BlockDriverState * bs)97779a55866SKevin Wolf static void GRAPH_RDLOCK blkdebug_refresh_filename(BlockDriverState *bs)
9782c31b04cSMax Reitz {
979036990d7SMax Reitz     BDRVBlkdebugState *s = bs->opaque;
9808779441bSMax Reitz     const QDictEntry *e;
981998b3a1eSMax Reitz     int ret;
9822c31b04cSMax Reitz 
983998b3a1eSMax Reitz     if (!bs->file->bs->exact_filename[0]) {
9842c31b04cSMax Reitz         return;
9852c31b04cSMax Reitz     }
9862c31b04cSMax Reitz 
987998b3a1eSMax Reitz     for (e = qdict_first(bs->full_open_options); e;
988998b3a1eSMax Reitz          e = qdict_next(bs->full_open_options, e))
989998b3a1eSMax Reitz     {
990998b3a1eSMax Reitz         /* Real child options are under "image", but "x-image" may
991998b3a1eSMax Reitz          * contain a filename */
992998b3a1eSMax Reitz         if (strcmp(qdict_entry_key(e), "config") &&
993998b3a1eSMax Reitz             strcmp(qdict_entry_key(e), "image") &&
994998b3a1eSMax Reitz             strcmp(qdict_entry_key(e), "x-image") &&
995998b3a1eSMax Reitz             strcmp(qdict_entry_key(e), "driver"))
996998b3a1eSMax Reitz         {
997998b3a1eSMax Reitz             return;
998998b3a1eSMax Reitz         }
999998b3a1eSMax Reitz     }
1000998b3a1eSMax Reitz 
1001998b3a1eSMax Reitz     ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
1002998b3a1eSMax Reitz                    "blkdebug:%s:%s",
1003998b3a1eSMax Reitz                    s->config_file ?: "", bs->file->bs->exact_filename);
1004de81d72dSMax Reitz     if (ret >= sizeof(bs->exact_filename)) {
1005de81d72dSMax Reitz         /* An overflow makes the filename unusable, so do not report any */
1006de81d72dSMax Reitz         bs->exact_filename[0] = 0;
1007de81d72dSMax Reitz     }
10088779441bSMax Reitz }
10098779441bSMax Reitz 
blkdebug_refresh_limits(BlockDriverState * bs,Error ** errp)1010835db3eeSEric Blake static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
1011835db3eeSEric Blake {
1012835db3eeSEric Blake     BDRVBlkdebugState *s = bs->opaque;
1013835db3eeSEric Blake 
1014835db3eeSEric Blake     if (s->align) {
1015a5b8dd2cSEric Blake         bs->bl.request_alignment = s->align;
1016835db3eeSEric Blake     }
1017430b26a8SEric Blake     if (s->max_transfer) {
1018430b26a8SEric Blake         bs->bl.max_transfer = s->max_transfer;
1019430b26a8SEric Blake     }
1020430b26a8SEric Blake     if (s->opt_write_zero) {
1021430b26a8SEric Blake         bs->bl.pwrite_zeroes_alignment = s->opt_write_zero;
1022430b26a8SEric Blake     }
1023430b26a8SEric Blake     if (s->max_write_zero) {
1024430b26a8SEric Blake         bs->bl.max_pwrite_zeroes = s->max_write_zero;
1025430b26a8SEric Blake     }
1026430b26a8SEric Blake     if (s->opt_discard) {
1027430b26a8SEric Blake         bs->bl.pdiscard_alignment = s->opt_discard;
1028430b26a8SEric Blake     }
1029430b26a8SEric Blake     if (s->max_discard) {
1030430b26a8SEric Blake         bs->bl.max_pdiscard = s->max_discard;
1031430b26a8SEric Blake     }
1032835db3eeSEric Blake }
1033835db3eeSEric Blake 
blkdebug_reopen_prepare(BDRVReopenState * reopen_state,BlockReopenQueue * queue,Error ** errp)1034c5e8bfb7SKevin Wolf static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
1035c5e8bfb7SKevin Wolf                                    BlockReopenQueue *queue, Error **errp)
1036c5e8bfb7SKevin Wolf {
1037c5e8bfb7SKevin Wolf     return 0;
1038c5e8bfb7SKevin Wolf }
1039c5e8bfb7SKevin Wolf 
blkdebug_child_perm(BlockDriverState * bs,BdrvChild * c,BdrvChildRole role,BlockReopenQueue * reopen_queue,uint64_t perm,uint64_t shared,uint64_t * nperm,uint64_t * nshared)104069c6449fSMax Reitz static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
1041bf8e925eSMax Reitz                                 BdrvChildRole role,
104269c6449fSMax Reitz                                 BlockReopenQueue *reopen_queue,
104369c6449fSMax Reitz                                 uint64_t perm, uint64_t shared,
104469c6449fSMax Reitz                                 uint64_t *nperm, uint64_t *nshared)
104569c6449fSMax Reitz {
104669c6449fSMax Reitz     BDRVBlkdebugState *s = bs->opaque;
104769c6449fSMax Reitz 
1048e5d8a406SMax Reitz     bdrv_default_perms(bs, c, role, reopen_queue,
1049bf8e925eSMax Reitz                        perm, shared, nperm, nshared);
105069c6449fSMax Reitz 
105169c6449fSMax Reitz     *nperm |= s->take_child_perms;
105269c6449fSMax Reitz     *nshared &= ~s->unshare_child_perms;
105369c6449fSMax Reitz }
105469c6449fSMax Reitz 
10552654267cSMax Reitz static const char *const blkdebug_strong_runtime_opts[] = {
10562654267cSMax Reitz     "config",
10572654267cSMax Reitz     "inject-error.",
10582654267cSMax Reitz     "set-state.",
10592654267cSMax Reitz     "align",
10602654267cSMax Reitz     "max-transfer",
10612654267cSMax Reitz     "opt-write-zero",
10622654267cSMax Reitz     "max-write-zero",
10632654267cSMax Reitz     "opt-discard",
10642654267cSMax Reitz     "max-discard",
10652654267cSMax Reitz 
10662654267cSMax Reitz     NULL
10672654267cSMax Reitz };
10682654267cSMax Reitz 
10696a143727SKevin Wolf static BlockDriver bdrv_blkdebug = {
10706a143727SKevin Wolf     .format_name            = "blkdebug",
10716a143727SKevin Wolf     .protocol_name          = "blkdebug",
10726a143727SKevin Wolf     .instance_size          = sizeof(BDRVBlkdebugState),
1073d8e12cd3SManos Pitsidianakis     .is_filter              = true,
10746a143727SKevin Wolf 
1075f4681212SKevin Wolf     .bdrv_parse_filename    = blkdebug_parse_filename,
107666f82ceeSKevin Wolf     .bdrv_file_open         = blkdebug_open,
10776a143727SKevin Wolf     .bdrv_close             = blkdebug_close,
1078c5e8bfb7SKevin Wolf     .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
107969c6449fSMax Reitz     .bdrv_child_perm        = blkdebug_child_perm,
1080d7010dfbSKevin Wolf 
1081c86422c5SEmanuele Giuseppe Esposito     .bdrv_co_getlength      = blkdebug_co_getlength,
10822c31b04cSMax Reitz     .bdrv_refresh_filename  = blkdebug_refresh_filename,
1083835db3eeSEric Blake     .bdrv_refresh_limits    = blkdebug_refresh_limits,
10846a143727SKevin Wolf 
10857c3a9985SKevin Wolf     .bdrv_co_preadv         = blkdebug_co_preadv,
10867c3a9985SKevin Wolf     .bdrv_co_pwritev        = blkdebug_co_pwritev,
10877c3a9985SKevin Wolf     .bdrv_co_flush_to_disk  = blkdebug_co_flush,
108863188c24SEric Blake     .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes,
108963188c24SEric Blake     .bdrv_co_pdiscard       = blkdebug_co_pdiscard,
10903e4d0e72SEric Blake     .bdrv_co_block_status   = blkdebug_co_block_status,
10918b9b0cc2SKevin Wolf 
1092c834dc05SEmanuele Giuseppe Esposito     .bdrv_co_debug_event        = blkdebug_co_debug_event,
10933c90c65dSKevin Wolf     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
10944cc70e93SFam Zheng     .bdrv_debug_remove_breakpoint
10954cc70e93SFam Zheng                                 = blkdebug_debug_remove_breakpoint,
10963c90c65dSKevin Wolf     .bdrv_debug_resume          = blkdebug_debug_resume,
10973c90c65dSKevin Wolf     .bdrv_debug_is_suspended    = blkdebug_debug_is_suspended,
10982654267cSMax Reitz 
10992654267cSMax Reitz     .strong_runtime_opts        = blkdebug_strong_runtime_opts,
11006a143727SKevin Wolf };
11016a143727SKevin Wolf 
bdrv_blkdebug_init(void)11026a143727SKevin Wolf static void bdrv_blkdebug_init(void)
11036a143727SKevin Wolf {
11046a143727SKevin Wolf     bdrv_register(&bdrv_blkdebug);
11056a143727SKevin Wolf }
11066a143727SKevin Wolf 
11076a143727SKevin Wolf block_init(bdrv_blkdebug_init);
1108