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,
1076*44b424dcSPaolo Bonzini .bdrv_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