Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Block protocol for I/O error injection |
| 3 | * |
| 4 | * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | #include "qemu-common.h" |
Paolo Bonzini | 1de7afc | 2012-12-17 18:20:00 +0100 | [diff] [blame] | 26 | #include "qemu/config-file.h" |
Paolo Bonzini | 737e150 | 2012-12-17 18:19:44 +0100 | [diff] [blame] | 27 | #include "block/block_int.h" |
Paolo Bonzini | 1de7afc | 2012-12-17 18:20:00 +0100 | [diff] [blame] | 28 | #include "qemu/module.h" |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 29 | #include "qapi/qmp/qbool.h" |
| 30 | #include "qapi/qmp/qdict.h" |
| 31 | #include "qapi/qmp/qint.h" |
| 32 | #include "qapi/qmp/qstring.h" |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 33 | |
| 34 | typedef struct BDRVBlkdebugState { |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 35 | int state; |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 36 | int new_state; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 37 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 38 | QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; |
| 39 | QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 40 | QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 41 | } BDRVBlkdebugState; |
| 42 | |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 43 | typedef struct BlkdebugAIOCB { |
Markus Armbruster | 7c84b1b | 2014-10-07 13:59:14 +0200 | [diff] [blame] | 44 | BlockAIOCB common; |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 45 | QEMUBH *bh; |
| 46 | int ret; |
| 47 | } BlkdebugAIOCB; |
| 48 | |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 49 | typedef struct BlkdebugSuspendedReq { |
| 50 | Coroutine *co; |
| 51 | char *tag; |
| 52 | QLIST_ENTRY(BlkdebugSuspendedReq) next; |
| 53 | } BlkdebugSuspendedReq; |
| 54 | |
Stefan Hajnoczi | d7331be | 2012-10-31 16:34:37 +0100 | [diff] [blame] | 55 | static const AIOCBInfo blkdebug_aiocb_info = { |
Fam Zheng | 4c78171 | 2014-09-11 13:41:17 +0800 | [diff] [blame] | 56 | .aiocb_size = sizeof(BlkdebugAIOCB), |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 57 | }; |
| 58 | |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 59 | enum { |
| 60 | ACTION_INJECT_ERROR, |
| 61 | ACTION_SET_STATE, |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 62 | ACTION_SUSPEND, |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 63 | }; |
| 64 | |
| 65 | typedef struct BlkdebugRule { |
| 66 | BlkDebugEvent event; |
| 67 | int action; |
| 68 | int state; |
| 69 | union { |
| 70 | struct { |
| 71 | int error; |
| 72 | int immediately; |
| 73 | int once; |
Paolo Bonzini | e4780db | 2012-06-06 08:10:43 +0200 | [diff] [blame] | 74 | int64_t sector; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 75 | } inject; |
| 76 | struct { |
| 77 | int new_state; |
| 78 | } set_state; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 79 | struct { |
| 80 | char *tag; |
| 81 | } suspend; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 82 | } options; |
| 83 | QLIST_ENTRY(BlkdebugRule) next; |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 84 | QSIMPLEQ_ENTRY(BlkdebugRule) active_next; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 85 | } BlkdebugRule; |
| 86 | |
| 87 | static QemuOptsList inject_error_opts = { |
| 88 | .name = "inject-error", |
| 89 | .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), |
| 90 | .desc = { |
| 91 | { |
| 92 | .name = "event", |
| 93 | .type = QEMU_OPT_STRING, |
| 94 | }, |
| 95 | { |
| 96 | .name = "state", |
| 97 | .type = QEMU_OPT_NUMBER, |
| 98 | }, |
| 99 | { |
| 100 | .name = "errno", |
| 101 | .type = QEMU_OPT_NUMBER, |
| 102 | }, |
| 103 | { |
Paolo Bonzini | e4780db | 2012-06-06 08:10:43 +0200 | [diff] [blame] | 104 | .name = "sector", |
| 105 | .type = QEMU_OPT_NUMBER, |
| 106 | }, |
| 107 | { |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 108 | .name = "once", |
| 109 | .type = QEMU_OPT_BOOL, |
| 110 | }, |
| 111 | { |
| 112 | .name = "immediately", |
| 113 | .type = QEMU_OPT_BOOL, |
| 114 | }, |
| 115 | { /* end of list */ } |
| 116 | }, |
| 117 | }; |
| 118 | |
| 119 | static QemuOptsList set_state_opts = { |
| 120 | .name = "set-state", |
Kevin Wolf | 327cdad | 2010-06-30 17:40:42 +0200 | [diff] [blame] | 121 | .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 122 | .desc = { |
| 123 | { |
| 124 | .name = "event", |
| 125 | .type = QEMU_OPT_STRING, |
| 126 | }, |
| 127 | { |
| 128 | .name = "state", |
| 129 | .type = QEMU_OPT_NUMBER, |
| 130 | }, |
| 131 | { |
| 132 | .name = "new_state", |
| 133 | .type = QEMU_OPT_NUMBER, |
| 134 | }, |
| 135 | { /* end of list */ } |
| 136 | }, |
| 137 | }; |
| 138 | |
| 139 | static QemuOptsList *config_groups[] = { |
| 140 | &inject_error_opts, |
| 141 | &set_state_opts, |
| 142 | NULL |
| 143 | }; |
| 144 | |
| 145 | static const char *event_names[BLKDBG_EVENT_MAX] = { |
Kevin Wolf | 8252278 | 2010-03-15 17:38:05 +0100 | [diff] [blame] | 146 | [BLKDBG_L1_UPDATE] = "l1_update", |
| 147 | [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table", |
| 148 | [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table", |
| 149 | [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table", |
| 150 | |
| 151 | [BLKDBG_L2_LOAD] = "l2_load", |
| 152 | [BLKDBG_L2_UPDATE] = "l2_update", |
| 153 | [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", |
| 154 | [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", |
| 155 | [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", |
| 156 | |
Kevin Wolf | 8252278 | 2010-03-15 17:38:05 +0100 | [diff] [blame] | 157 | [BLKDBG_READ_AIO] = "read_aio", |
Kevin Wolf | 8252278 | 2010-03-15 17:38:05 +0100 | [diff] [blame] | 158 | [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", |
| 159 | [BLKDBG_READ_COMPRESSED] = "read_compressed", |
| 160 | |
| 161 | [BLKDBG_WRITE_AIO] = "write_aio", |
| 162 | [BLKDBG_WRITE_COMPRESSED] = "write_compressed", |
| 163 | |
| 164 | [BLKDBG_VMSTATE_LOAD] = "vmstate_load", |
| 165 | [BLKDBG_VMSTATE_SAVE] = "vmstate_save", |
| 166 | |
| 167 | [BLKDBG_COW_READ] = "cow_read", |
| 168 | [BLKDBG_COW_WRITE] = "cow_write", |
| 169 | |
| 170 | [BLKDBG_REFTABLE_LOAD] = "reftable_load", |
| 171 | [BLKDBG_REFTABLE_GROW] = "reftable_grow", |
Max Reitz | afa5019 | 2013-09-02 09:25:10 +0200 | [diff] [blame] | 172 | [BLKDBG_REFTABLE_UPDATE] = "reftable_update", |
Kevin Wolf | 8252278 | 2010-03-15 17:38:05 +0100 | [diff] [blame] | 173 | |
| 174 | [BLKDBG_REFBLOCK_LOAD] = "refblock_load", |
| 175 | [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", |
| 176 | [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", |
| 177 | [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", |
| 178 | [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup", |
| 179 | [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write", |
| 180 | [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks", |
| 181 | [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table", |
| 182 | [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table", |
| 183 | |
| 184 | [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", |
| 185 | [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", |
| 186 | [BLKDBG_CLUSTER_FREE] = "cluster_free", |
Kevin Wolf | bf736fe | 2013-06-05 15:17:55 +0200 | [diff] [blame] | 187 | |
| 188 | [BLKDBG_FLUSH_TO_OS] = "flush_to_os", |
| 189 | [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", |
Kevin Wolf | 9e1cb96 | 2014-01-14 15:37:03 +0100 | [diff] [blame] | 190 | |
| 191 | [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head", |
| 192 | [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head", |
| 193 | [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail", |
| 194 | [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail", |
| 195 | [BLKDBG_PWRITEV] = "pwritev", |
| 196 | [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", |
| 197 | [BLKDBG_PWRITEV_DONE] = "pwritev_done", |
Max Reitz | 9405418 | 2014-10-24 15:57:32 +0200 | [diff] [blame] | 198 | |
| 199 | [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 200 | }; |
| 201 | |
| 202 | static int get_event_by_name(const char *name, BlkDebugEvent *event) |
| 203 | { |
| 204 | int i; |
| 205 | |
| 206 | for (i = 0; i < BLKDBG_EVENT_MAX; i++) { |
| 207 | if (!strcmp(event_names[i], name)) { |
| 208 | *event = i; |
| 209 | return 0; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | return -1; |
| 214 | } |
| 215 | |
| 216 | struct add_rule_data { |
| 217 | BDRVBlkdebugState *s; |
| 218 | int action; |
| 219 | }; |
| 220 | |
Markus Armbruster | 28d0de7 | 2015-03-13 13:35:14 +0100 | [diff] [blame] | 221 | static int add_rule(void *opaque, QemuOpts *opts, Error **errp) |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 222 | { |
| 223 | struct add_rule_data *d = opaque; |
| 224 | BDRVBlkdebugState *s = d->s; |
| 225 | const char* event_name; |
| 226 | BlkDebugEvent event; |
| 227 | struct BlkdebugRule *rule; |
| 228 | |
| 229 | /* Find the right event for the rule */ |
| 230 | event_name = qemu_opt_get(opts, "event"); |
Stefan Hajnoczi | d4362d6 | 2014-09-20 09:55:52 +0100 | [diff] [blame] | 231 | if (!event_name) { |
Markus Armbruster | 8809cfc | 2015-03-13 13:38:42 +0100 | [diff] [blame] | 232 | error_setg(errp, "Missing event name for rule"); |
Stefan Hajnoczi | d4362d6 | 2014-09-20 09:55:52 +0100 | [diff] [blame] | 233 | return -1; |
| 234 | } else if (get_event_by_name(event_name, &event) < 0) { |
Markus Armbruster | 8809cfc | 2015-03-13 13:38:42 +0100 | [diff] [blame] | 235 | error_setg(errp, "Invalid event name \"%s\"", event_name); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 236 | return -1; |
| 237 | } |
| 238 | |
| 239 | /* Set attributes common for all actions */ |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 240 | rule = g_malloc0(sizeof(*rule)); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 241 | *rule = (struct BlkdebugRule) { |
| 242 | .event = event, |
| 243 | .action = d->action, |
| 244 | .state = qemu_opt_get_number(opts, "state", 0), |
| 245 | }; |
| 246 | |
| 247 | /* Parse action-specific options */ |
| 248 | switch (d->action) { |
| 249 | case ACTION_INJECT_ERROR: |
| 250 | rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); |
| 251 | rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); |
| 252 | rule->options.inject.immediately = |
| 253 | qemu_opt_get_bool(opts, "immediately", 0); |
Paolo Bonzini | e4780db | 2012-06-06 08:10:43 +0200 | [diff] [blame] | 254 | rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 255 | break; |
| 256 | |
| 257 | case ACTION_SET_STATE: |
| 258 | rule->options.set_state.new_state = |
| 259 | qemu_opt_get_number(opts, "new_state", 0); |
| 260 | break; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 261 | |
| 262 | case ACTION_SUSPEND: |
| 263 | rule->options.suspend.tag = |
| 264 | g_strdup(qemu_opt_get(opts, "tag")); |
| 265 | break; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 266 | }; |
| 267 | |
| 268 | /* Add the rule */ |
| 269 | QLIST_INSERT_HEAD(&s->rules[event], rule, next); |
| 270 | |
| 271 | return 0; |
| 272 | } |
| 273 | |
Kevin Wolf | 9e35542 | 2012-12-06 14:32:56 +0100 | [diff] [blame] | 274 | static void remove_rule(BlkdebugRule *rule) |
| 275 | { |
| 276 | switch (rule->action) { |
| 277 | case ACTION_INJECT_ERROR: |
| 278 | case ACTION_SET_STATE: |
| 279 | break; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 280 | case ACTION_SUSPEND: |
| 281 | g_free(rule->options.suspend.tag); |
| 282 | break; |
Kevin Wolf | 9e35542 | 2012-12-06 14:32:56 +0100 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | QLIST_REMOVE(rule, next); |
| 286 | g_free(rule); |
| 287 | } |
| 288 | |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 289 | static int read_config(BDRVBlkdebugState *s, const char *filename, |
| 290 | QDict *options, Error **errp) |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 291 | { |
Max Reitz | 85a040e | 2013-12-20 19:28:06 +0100 | [diff] [blame] | 292 | FILE *f = NULL; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 293 | int ret; |
| 294 | struct add_rule_data d; |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 295 | Error *local_err = NULL; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 296 | |
Max Reitz | 85a040e | 2013-12-20 19:28:06 +0100 | [diff] [blame] | 297 | if (filename) { |
| 298 | f = fopen(filename, "r"); |
| 299 | if (f == NULL) { |
| 300 | error_setg_errno(errp, errno, "Could not read blkdebug config file"); |
| 301 | return -errno; |
| 302 | } |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 303 | |
Max Reitz | 85a040e | 2013-12-20 19:28:06 +0100 | [diff] [blame] | 304 | ret = qemu_config_parse(f, config_groups, filename); |
| 305 | if (ret < 0) { |
| 306 | error_setg(errp, "Could not parse blkdebug config file"); |
| 307 | ret = -EINVAL; |
| 308 | goto fail; |
| 309 | } |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 310 | } |
| 311 | |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 312 | qemu_config_parse_qdict(options, config_groups, &local_err); |
Markus Armbruster | 84d18f0 | 2014-01-30 15:07:28 +0100 | [diff] [blame] | 313 | if (local_err) { |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 314 | error_propagate(errp, local_err); |
| 315 | ret = -EINVAL; |
| 316 | goto fail; |
| 317 | } |
| 318 | |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 319 | d.s = s; |
| 320 | d.action = ACTION_INJECT_ERROR; |
Markus Armbruster | 8809cfc | 2015-03-13 13:38:42 +0100 | [diff] [blame] | 321 | qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err); |
Stefan Hajnoczi | d4362d6 | 2014-09-20 09:55:52 +0100 | [diff] [blame] | 322 | if (local_err) { |
| 323 | error_propagate(errp, local_err); |
| 324 | ret = -EINVAL; |
| 325 | goto fail; |
| 326 | } |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 327 | |
| 328 | d.action = ACTION_SET_STATE; |
Markus Armbruster | 8809cfc | 2015-03-13 13:38:42 +0100 | [diff] [blame] | 329 | qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err); |
Stefan Hajnoczi | d4362d6 | 2014-09-20 09:55:52 +0100 | [diff] [blame] | 330 | if (local_err) { |
| 331 | error_propagate(errp, local_err); |
| 332 | ret = -EINVAL; |
| 333 | goto fail; |
| 334 | } |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 335 | |
| 336 | ret = 0; |
| 337 | fail: |
Kevin Wolf | 698f0d5 | 2010-06-30 17:42:23 +0200 | [diff] [blame] | 338 | qemu_opts_reset(&inject_error_opts); |
| 339 | qemu_opts_reset(&set_state_opts); |
Max Reitz | 85a040e | 2013-12-20 19:28:06 +0100 | [diff] [blame] | 340 | if (f) { |
| 341 | fclose(f); |
| 342 | } |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 343 | return ret; |
| 344 | } |
| 345 | |
| 346 | /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 347 | static void blkdebug_parse_filename(const char *filename, QDict *options, |
| 348 | Error **errp) |
| 349 | { |
| 350 | const char *c; |
| 351 | |
| 352 | /* Parse the blkdebug: prefix */ |
| 353 | if (!strstart(filename, "blkdebug:", &filename)) { |
Max Reitz | d4881b9 | 2013-12-20 19:28:02 +0100 | [diff] [blame] | 354 | /* There was no prefix; therefore, all options have to be already |
| 355 | present in the QDict (except for the filename) */ |
| 356 | qdict_put(options, "x-image", qstring_from_str(filename)); |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 357 | return; |
| 358 | } |
| 359 | |
| 360 | /* Parse config file path */ |
| 361 | c = strchr(filename, ':'); |
| 362 | if (c == NULL) { |
| 363 | error_setg(errp, "blkdebug requires both config file and image path"); |
| 364 | return; |
| 365 | } |
| 366 | |
| 367 | if (c != filename) { |
| 368 | QString *config_path; |
| 369 | config_path = qstring_from_substr(filename, 0, c - filename - 1); |
| 370 | qdict_put(options, "config", config_path); |
| 371 | } |
| 372 | |
| 373 | /* TODO Allow multi-level nesting and set file.filename here */ |
| 374 | filename = c + 1; |
| 375 | qdict_put(options, "x-image", qstring_from_str(filename)); |
| 376 | } |
| 377 | |
| 378 | static QemuOptsList runtime_opts = { |
| 379 | .name = "blkdebug", |
| 380 | .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |
| 381 | .desc = { |
| 382 | { |
| 383 | .name = "config", |
| 384 | .type = QEMU_OPT_STRING, |
| 385 | .help = "Path to the configuration file", |
| 386 | }, |
| 387 | { |
| 388 | .name = "x-image", |
| 389 | .type = QEMU_OPT_STRING, |
| 390 | .help = "[internal use only, will be removed]", |
| 391 | }, |
Kevin Wolf | b35ee7f | 2014-01-14 13:44:35 +0100 | [diff] [blame] | 392 | { |
| 393 | .name = "align", |
| 394 | .type = QEMU_OPT_SIZE, |
| 395 | .help = "Required alignment in bytes", |
| 396 | }, |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 397 | { /* end of list */ } |
| 398 | }, |
| 399 | }; |
| 400 | |
Max Reitz | 015a103 | 2013-09-05 14:22:29 +0200 | [diff] [blame] | 401 | static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, |
| 402 | Error **errp) |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 403 | { |
| 404 | BDRVBlkdebugState *s = bs->opaque; |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 405 | QemuOpts *opts; |
| 406 | Error *local_err = NULL; |
Max Reitz | 4373593 | 2013-12-20 19:28:15 +0100 | [diff] [blame] | 407 | const char *config; |
Kevin Wolf | b35ee7f | 2014-01-14 13:44:35 +0100 | [diff] [blame] | 408 | uint64_t align; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 409 | int ret; |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 410 | |
Peter Crosthwaite | 87ea75d | 2014-01-01 18:49:17 -0800 | [diff] [blame] | 411 | opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 412 | qemu_opts_absorb_qdict(opts, options, &local_err); |
Markus Armbruster | 84d18f0 | 2014-01-30 15:07:28 +0100 | [diff] [blame] | 413 | if (local_err) { |
Max Reitz | 10ffa72 | 2013-10-10 15:44:03 +0200 | [diff] [blame] | 414 | error_propagate(errp, local_err); |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 415 | ret = -EINVAL; |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 416 | goto out; |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 417 | } |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 418 | |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 419 | /* Read rules from config file or command line options */ |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 420 | config = qemu_opt_get(opts, "config"); |
Max Reitz | 89f2b21 | 2013-12-20 19:28:07 +0100 | [diff] [blame] | 421 | ret = read_config(s, config, options, errp); |
Max Reitz | 85a040e | 2013-12-20 19:28:06 +0100 | [diff] [blame] | 422 | if (ret) { |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 423 | goto out; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 424 | } |
| 425 | |
Kevin Wolf | 8db520c | 2010-06-30 17:43:40 +0200 | [diff] [blame] | 426 | /* Set initial state */ |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 427 | s->state = 1; |
Kevin Wolf | 8db520c | 2010-06-30 17:43:40 +0200 | [diff] [blame] | 428 | |
Fam Zheng | 6b826af | 2015-10-16 18:46:04 +0800 | [diff] [blame] | 429 | /* Open the image file */ |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 430 | bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", |
| 431 | bs, &child_file, false, &local_err); |
| 432 | if (local_err) { |
| 433 | ret = -EINVAL; |
Max Reitz | 10ffa72 | 2013-10-10 15:44:03 +0200 | [diff] [blame] | 434 | error_propagate(errp, local_err); |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 435 | goto out; |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 436 | } |
| 437 | |
Kevin Wolf | b35ee7f | 2014-01-14 13:44:35 +0100 | [diff] [blame] | 438 | /* Set request alignment */ |
| 439 | align = qemu_opt_get_size(opts, "align", bs->request_alignment); |
| 440 | if (align > 0 && align < INT_MAX && !(align & (align - 1))) { |
| 441 | bs->request_alignment = align; |
| 442 | } else { |
| 443 | error_setg(errp, "Invalid alignment"); |
| 444 | ret = -EINVAL; |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 445 | goto fail_unref; |
Kevin Wolf | b35ee7f | 2014-01-14 13:44:35 +0100 | [diff] [blame] | 446 | } |
| 447 | |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 448 | ret = 0; |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 449 | goto out; |
| 450 | |
| 451 | fail_unref: |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 452 | bdrv_unref_child(bs, bs->file); |
Kevin Wolf | eaf944a | 2014-02-08 09:53:22 +0100 | [diff] [blame] | 453 | out: |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 454 | qemu_opts_del(opts); |
| 455 | return ret; |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 456 | } |
| 457 | |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 458 | static void error_callback_bh(void *opaque) |
| 459 | { |
| 460 | struct BlkdebugAIOCB *acb = opaque; |
| 461 | qemu_bh_delete(acb->bh); |
| 462 | acb->common.cb(acb->common.opaque, acb->ret); |
Fam Zheng | 8007429 | 2014-09-11 13:41:28 +0800 | [diff] [blame] | 463 | qemu_aio_unref(acb); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 464 | } |
| 465 | |
Markus Armbruster | 7c84b1b | 2014-10-07 13:59:14 +0200 | [diff] [blame] | 466 | static BlockAIOCB *inject_error(BlockDriverState *bs, |
Markus Armbruster | 097310b | 2014-10-07 13:59:15 +0200 | [diff] [blame] | 467 | BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule) |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 468 | { |
| 469 | BDRVBlkdebugState *s = bs->opaque; |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 470 | int error = rule->options.inject.error; |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 471 | struct BlkdebugAIOCB *acb; |
| 472 | QEMUBH *bh; |
John Snow | a069e2f | 2015-02-06 16:26:17 -0500 | [diff] [blame] | 473 | bool immediately = rule->options.inject.immediately; |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 474 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 475 | if (rule->options.inject.once) { |
John Snow | a069e2f | 2015-02-06 16:26:17 -0500 | [diff] [blame] | 476 | QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); |
| 477 | remove_rule(rule); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 478 | } |
| 479 | |
John Snow | a069e2f | 2015-02-06 16:26:17 -0500 | [diff] [blame] | 480 | if (immediately) { |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 481 | return NULL; |
| 482 | } |
| 483 | |
Stefan Hajnoczi | d7331be | 2012-10-31 16:34:37 +0100 | [diff] [blame] | 484 | acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 485 | acb->ret = -error; |
| 486 | |
Stefan Hajnoczi | 7e1efdf | 2014-05-08 16:34:38 +0200 | [diff] [blame] | 487 | bh = aio_bh_new(bdrv_get_aio_context(bs), error_callback_bh, acb); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 488 | acb->bh = bh; |
| 489 | qemu_bh_schedule(bh); |
| 490 | |
Kevin Wolf | b666d23 | 2010-05-05 11:44:39 +0200 | [diff] [blame] | 491 | return &acb->common; |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 492 | } |
| 493 | |
Markus Armbruster | 7c84b1b | 2014-10-07 13:59:14 +0200 | [diff] [blame] | 494 | static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 495 | int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |
Markus Armbruster | 097310b | 2014-10-07 13:59:15 +0200 | [diff] [blame] | 496 | BlockCompletionFunc *cb, void *opaque) |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 497 | { |
| 498 | BDRVBlkdebugState *s = bs->opaque; |
Paolo Bonzini | e4780db | 2012-06-06 08:10:43 +0200 | [diff] [blame] | 499 | BlkdebugRule *rule = NULL; |
| 500 | |
| 501 | QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { |
| 502 | if (rule->options.inject.sector == -1 || |
| 503 | (rule->options.inject.sector >= sector_num && |
| 504 | rule->options.inject.sector < sector_num + nb_sectors)) { |
| 505 | break; |
| 506 | } |
| 507 | } |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 508 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 509 | if (rule && rule->options.inject.error) { |
| 510 | return inject_error(bs, cb, opaque, rule); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 511 | } |
| 512 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 513 | return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors, |
| 514 | cb, opaque); |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 515 | } |
| 516 | |
Markus Armbruster | 7c84b1b | 2014-10-07 13:59:14 +0200 | [diff] [blame] | 517 | static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 518 | int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, |
Markus Armbruster | 097310b | 2014-10-07 13:59:15 +0200 | [diff] [blame] | 519 | BlockCompletionFunc *cb, void *opaque) |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 520 | { |
| 521 | BDRVBlkdebugState *s = bs->opaque; |
Paolo Bonzini | e4780db | 2012-06-06 08:10:43 +0200 | [diff] [blame] | 522 | BlkdebugRule *rule = NULL; |
| 523 | |
| 524 | QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { |
| 525 | if (rule->options.inject.sector == -1 || |
| 526 | (rule->options.inject.sector >= sector_num && |
| 527 | rule->options.inject.sector < sector_num + nb_sectors)) { |
| 528 | break; |
| 529 | } |
| 530 | } |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 531 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 532 | if (rule && rule->options.inject.error) { |
| 533 | return inject_error(bs, cb, opaque, rule); |
Kevin Wolf | b9f66d9 | 2010-02-19 16:24:35 +0100 | [diff] [blame] | 534 | } |
| 535 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 536 | return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, |
| 537 | cb, opaque); |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 538 | } |
| 539 | |
Markus Armbruster | 7c84b1b | 2014-10-07 13:59:14 +0200 | [diff] [blame] | 540 | static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, |
Markus Armbruster | 097310b | 2014-10-07 13:59:15 +0200 | [diff] [blame] | 541 | BlockCompletionFunc *cb, void *opaque) |
Paolo Bonzini | 9e52c53 | 2014-08-04 17:11:02 -0400 | [diff] [blame] | 542 | { |
| 543 | BDRVBlkdebugState *s = bs->opaque; |
| 544 | BlkdebugRule *rule = NULL; |
| 545 | |
| 546 | QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { |
| 547 | if (rule->options.inject.sector == -1) { |
| 548 | break; |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | if (rule && rule->options.inject.error) { |
| 553 | return inject_error(bs, cb, opaque, rule); |
| 554 | } |
| 555 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 556 | return bdrv_aio_flush(bs->file->bs, cb, opaque); |
Paolo Bonzini | 9e52c53 | 2014-08-04 17:11:02 -0400 | [diff] [blame] | 557 | } |
| 558 | |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 559 | |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 560 | static void blkdebug_close(BlockDriverState *bs) |
| 561 | { |
| 562 | BDRVBlkdebugState *s = bs->opaque; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 563 | BlkdebugRule *rule, *next; |
| 564 | int i; |
| 565 | |
| 566 | for (i = 0; i < BLKDBG_EVENT_MAX; i++) { |
| 567 | QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { |
Kevin Wolf | 9e35542 | 2012-12-06 14:32:56 +0100 | [diff] [blame] | 568 | remove_rule(rule); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 569 | } |
| 570 | } |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 571 | } |
| 572 | |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 573 | static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) |
| 574 | { |
| 575 | BDRVBlkdebugState *s = bs->opaque; |
| 576 | BlkdebugSuspendedReq r; |
| 577 | |
| 578 | r = (BlkdebugSuspendedReq) { |
| 579 | .co = qemu_coroutine_self(), |
| 580 | .tag = g_strdup(rule->options.suspend.tag), |
| 581 | }; |
| 582 | |
| 583 | remove_rule(rule); |
| 584 | QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); |
| 585 | |
| 586 | printf("blkdebug: Suspended request '%s'\n", r.tag); |
| 587 | qemu_coroutine_yield(); |
| 588 | printf("blkdebug: Resuming request '%s'\n", r.tag); |
| 589 | |
| 590 | QLIST_REMOVE(&r, next); |
| 591 | g_free(r.tag); |
| 592 | } |
| 593 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 594 | static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 595 | bool injected) |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 596 | { |
| 597 | BDRVBlkdebugState *s = bs->opaque; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 598 | |
| 599 | /* Only process rules for the current state */ |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 600 | if (rule->state && rule->state != s->state) { |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 601 | return injected; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 602 | } |
| 603 | |
| 604 | /* Take the action */ |
| 605 | switch (rule->action) { |
| 606 | case ACTION_INJECT_ERROR: |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 607 | if (!injected) { |
| 608 | QSIMPLEQ_INIT(&s->active_rules); |
| 609 | injected = true; |
| 610 | } |
| 611 | QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 612 | break; |
| 613 | |
| 614 | case ACTION_SET_STATE: |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 615 | s->new_state = rule->options.set_state.new_state; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 616 | break; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 617 | |
| 618 | case ACTION_SUSPEND: |
| 619 | suspend_request(bs, rule); |
| 620 | break; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 621 | } |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 622 | return injected; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 623 | } |
| 624 | |
| 625 | static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) |
| 626 | { |
| 627 | BDRVBlkdebugState *s = bs->opaque; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 628 | struct BlkdebugRule *rule, *next; |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 629 | bool injected; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 630 | |
Blue Swirl | 95ee391 | 2010-09-18 05:53:15 +0000 | [diff] [blame] | 631 | assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 632 | |
Paolo Bonzini | 571cd43 | 2012-06-06 08:10:42 +0200 | [diff] [blame] | 633 | injected = false; |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 634 | s->new_state = s->state; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 635 | QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) { |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 636 | injected = process_rule(bs, rule, injected); |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 637 | } |
Paolo Bonzini | 8f96b5b | 2012-09-28 17:23:00 +0200 | [diff] [blame] | 638 | s->state = s->new_state; |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 639 | } |
| 640 | |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 641 | static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, |
| 642 | const char *tag) |
| 643 | { |
| 644 | BDRVBlkdebugState *s = bs->opaque; |
| 645 | struct BlkdebugRule *rule; |
| 646 | BlkDebugEvent blkdebug_event; |
| 647 | |
| 648 | if (get_event_by_name(event, &blkdebug_event) < 0) { |
| 649 | return -ENOENT; |
| 650 | } |
| 651 | |
| 652 | |
| 653 | rule = g_malloc(sizeof(*rule)); |
| 654 | *rule = (struct BlkdebugRule) { |
| 655 | .event = blkdebug_event, |
| 656 | .action = ACTION_SUSPEND, |
| 657 | .state = 0, |
| 658 | .options.suspend.tag = g_strdup(tag), |
| 659 | }; |
| 660 | |
| 661 | QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next); |
| 662 | |
| 663 | return 0; |
| 664 | } |
| 665 | |
| 666 | static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag) |
| 667 | { |
| 668 | BDRVBlkdebugState *s = bs->opaque; |
Fam Zheng | c547e56 | 2013-12-13 15:25:12 +0800 | [diff] [blame] | 669 | BlkdebugSuspendedReq *r, *next; |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 670 | |
Fam Zheng | c547e56 | 2013-12-13 15:25:12 +0800 | [diff] [blame] | 671 | QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) { |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 672 | if (!strcmp(r->tag, tag)) { |
| 673 | qemu_coroutine_enter(r->co, NULL); |
| 674 | return 0; |
| 675 | } |
| 676 | } |
| 677 | return -ENOENT; |
| 678 | } |
| 679 | |
Fam Zheng | 4cc70e9 | 2013-11-20 10:01:54 +0800 | [diff] [blame] | 680 | static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, |
| 681 | const char *tag) |
| 682 | { |
| 683 | BDRVBlkdebugState *s = bs->opaque; |
Fam Zheng | c547e56 | 2013-12-13 15:25:12 +0800 | [diff] [blame] | 684 | BlkdebugSuspendedReq *r, *r_next; |
Fam Zheng | 4cc70e9 | 2013-11-20 10:01:54 +0800 | [diff] [blame] | 685 | BlkdebugRule *rule, *next; |
| 686 | int i, ret = -ENOENT; |
| 687 | |
| 688 | for (i = 0; i < BLKDBG_EVENT_MAX; i++) { |
| 689 | QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { |
| 690 | if (rule->action == ACTION_SUSPEND && |
| 691 | !strcmp(rule->options.suspend.tag, tag)) { |
| 692 | remove_rule(rule); |
| 693 | ret = 0; |
| 694 | } |
| 695 | } |
| 696 | } |
Fam Zheng | c547e56 | 2013-12-13 15:25:12 +0800 | [diff] [blame] | 697 | QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) { |
Fam Zheng | 4cc70e9 | 2013-11-20 10:01:54 +0800 | [diff] [blame] | 698 | if (!strcmp(r->tag, tag)) { |
| 699 | qemu_coroutine_enter(r->co, NULL); |
| 700 | ret = 0; |
| 701 | } |
| 702 | } |
| 703 | return ret; |
| 704 | } |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 705 | |
| 706 | static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) |
| 707 | { |
| 708 | BDRVBlkdebugState *s = bs->opaque; |
| 709 | BlkdebugSuspendedReq *r; |
| 710 | |
| 711 | QLIST_FOREACH(r, &s->suspended_reqs, next) { |
| 712 | if (!strcmp(r->tag, tag)) { |
| 713 | return true; |
| 714 | } |
| 715 | } |
| 716 | return false; |
| 717 | } |
| 718 | |
Paolo Bonzini | e130225 | 2012-06-06 08:10:41 +0200 | [diff] [blame] | 719 | static int64_t blkdebug_getlength(BlockDriverState *bs) |
| 720 | { |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 721 | return bdrv_getlength(bs->file->bs); |
Paolo Bonzini | e130225 | 2012-06-06 08:10:41 +0200 | [diff] [blame] | 722 | } |
| 723 | |
Kevin Wolf | 8eedfbd | 2015-04-14 16:32:45 +0200 | [diff] [blame] | 724 | static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) |
| 725 | { |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 726 | return bdrv_truncate(bs->file->bs, offset); |
Kevin Wolf | 8eedfbd | 2015-04-14 16:32:45 +0200 | [diff] [blame] | 727 | } |
| 728 | |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 729 | static void blkdebug_refresh_filename(BlockDriverState *bs) |
| 730 | { |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 731 | QDict *opts; |
Max Reitz | 8779441 | 2014-11-11 10:23:44 +0100 | [diff] [blame] | 732 | const QDictEntry *e; |
| 733 | bool force_json = false; |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 734 | |
Max Reitz | 8779441 | 2014-11-11 10:23:44 +0100 | [diff] [blame] | 735 | for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { |
| 736 | if (strcmp(qdict_entry_key(e), "config") && |
| 737 | strcmp(qdict_entry_key(e), "x-image") && |
| 738 | strcmp(qdict_entry_key(e), "image") && |
| 739 | strncmp(qdict_entry_key(e), "image.", strlen("image."))) |
| 740 | { |
| 741 | force_json = true; |
| 742 | break; |
| 743 | } |
| 744 | } |
| 745 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 746 | if (force_json && !bs->file->bs->full_open_options) { |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 747 | /* The config file cannot be recreated, so creating a plain filename |
| 748 | * is impossible */ |
| 749 | return; |
| 750 | } |
| 751 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 752 | if (!force_json && bs->file->bs->exact_filename[0]) { |
Max Reitz | 8779441 | 2014-11-11 10:23:44 +0100 | [diff] [blame] | 753 | snprintf(bs->exact_filename, sizeof(bs->exact_filename), |
| 754 | "blkdebug:%s:%s", |
| 755 | qdict_get_try_str(bs->options, "config") ?: "", |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 756 | bs->file->bs->exact_filename); |
Max Reitz | 8779441 | 2014-11-11 10:23:44 +0100 | [diff] [blame] | 757 | } |
| 758 | |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 759 | opts = qdict_new(); |
| 760 | qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); |
| 761 | |
Kevin Wolf | 9a4f4c3 | 2015-06-16 14:19:22 +0200 | [diff] [blame] | 762 | QINCREF(bs->file->bs->full_open_options); |
| 763 | qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options)); |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 764 | |
Max Reitz | 8779441 | 2014-11-11 10:23:44 +0100 | [diff] [blame] | 765 | for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { |
| 766 | if (strcmp(qdict_entry_key(e), "x-image") && |
| 767 | strcmp(qdict_entry_key(e), "image") && |
| 768 | strncmp(qdict_entry_key(e), "image.", strlen("image."))) |
| 769 | { |
| 770 | qobject_incref(qdict_entry_value(e)); |
| 771 | qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 772 | } |
| 773 | } |
| 774 | |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 775 | bs->full_open_options = opts; |
| 776 | } |
| 777 | |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 778 | static BlockDriver bdrv_blkdebug = { |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 779 | .format_name = "blkdebug", |
| 780 | .protocol_name = "blkdebug", |
| 781 | .instance_size = sizeof(BDRVBlkdebugState), |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 782 | |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 783 | .bdrv_parse_filename = blkdebug_parse_filename, |
| 784 | .bdrv_file_open = blkdebug_open, |
| 785 | .bdrv_close = blkdebug_close, |
| 786 | .bdrv_getlength = blkdebug_getlength, |
Kevin Wolf | 8eedfbd | 2015-04-14 16:32:45 +0200 | [diff] [blame] | 787 | .bdrv_truncate = blkdebug_truncate, |
Max Reitz | 2c31b04 | 2014-07-18 20:24:57 +0200 | [diff] [blame] | 788 | .bdrv_refresh_filename = blkdebug_refresh_filename, |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 789 | |
Kevin Wolf | f468121 | 2013-04-10 13:37:33 +0200 | [diff] [blame] | 790 | .bdrv_aio_readv = blkdebug_aio_readv, |
| 791 | .bdrv_aio_writev = blkdebug_aio_writev, |
Paolo Bonzini | 9e52c53 | 2014-08-04 17:11:02 -0400 | [diff] [blame] | 792 | .bdrv_aio_flush = blkdebug_aio_flush, |
Kevin Wolf | 8b9b0cc | 2010-03-15 17:27:00 +0100 | [diff] [blame] | 793 | |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 794 | .bdrv_debug_event = blkdebug_debug_event, |
| 795 | .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, |
Fam Zheng | 4cc70e9 | 2013-11-20 10:01:54 +0800 | [diff] [blame] | 796 | .bdrv_debug_remove_breakpoint |
| 797 | = blkdebug_debug_remove_breakpoint, |
Kevin Wolf | 3c90c65 | 2012-12-06 14:32:57 +0100 | [diff] [blame] | 798 | .bdrv_debug_resume = blkdebug_debug_resume, |
| 799 | .bdrv_debug_is_suspended = blkdebug_debug_is_suspended, |
Kevin Wolf | 6a14372 | 2010-02-18 17:48:12 +0100 | [diff] [blame] | 800 | }; |
| 801 | |
| 802 | static void bdrv_blkdebug_init(void) |
| 803 | { |
| 804 | bdrv_register(&bdrv_blkdebug); |
| 805 | } |
| 806 | |
| 807 | block_init(bdrv_blkdebug_init); |