1 | /* |
2 | * Block protocol for I/O error injection |
3 | * |
4 | * Copyright (C) 2016-2017 Red Hat, Inc. |
5 | * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | * of this software and associated documentation files (the "Software"), to deal |
9 | * in the Software without restriction, including without limitation the rights |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | * copies of the Software, and to permit persons to whom the Software is |
12 | * furnished to do so, subject to the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice shall be included in |
15 | * all copies or substantial portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
23 | * THE SOFTWARE. |
24 | */ |
25 | |
26 | #include "qemu/osdep.h" |
27 | #include "qapi/error.h" |
28 | #include "qemu/cutils.h" |
29 | #include "qemu/config-file.h" |
30 | #include "block/block_int.h" |
31 | #include "qemu/module.h" |
32 | #include "qemu/option.h" |
33 | #include "qapi/qmp/qdict.h" |
34 | #include "qapi/qmp/qstring.h" |
35 | #include "sysemu/qtest.h" |
36 | |
37 | typedef struct BDRVBlkdebugState { |
38 | int state; |
39 | int new_state; |
40 | uint64_t align; |
41 | uint64_t max_transfer; |
42 | uint64_t opt_write_zero; |
43 | uint64_t max_write_zero; |
44 | uint64_t opt_discard; |
45 | uint64_t max_discard; |
46 | |
47 | /* For blkdebug_refresh_filename() */ |
48 | char *config_file; |
49 | |
50 | QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX]; |
51 | QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; |
52 | QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; |
53 | } BDRVBlkdebugState; |
54 | |
55 | typedef struct BlkdebugAIOCB { |
56 | BlockAIOCB common; |
57 | int ret; |
58 | } BlkdebugAIOCB; |
59 | |
60 | typedef struct BlkdebugSuspendedReq { |
61 | Coroutine *co; |
62 | char *tag; |
63 | QLIST_ENTRY(BlkdebugSuspendedReq) next; |
64 | } BlkdebugSuspendedReq; |
65 | |
66 | enum { |
67 | ACTION_INJECT_ERROR, |
68 | ACTION_SET_STATE, |
69 | ACTION_SUSPEND, |
70 | }; |
71 | |
72 | typedef struct BlkdebugRule { |
73 | BlkdebugEvent event; |
74 | int action; |
75 | int state; |
76 | union { |
77 | struct { |
78 | uint64_t iotype_mask; |
79 | int error; |
80 | int immediately; |
81 | int once; |
82 | int64_t offset; |
83 | } inject; |
84 | struct { |
85 | int new_state; |
86 | } set_state; |
87 | struct { |
88 | char *tag; |
89 | } suspend; |
90 | } options; |
91 | QLIST_ENTRY(BlkdebugRule) next; |
92 | QSIMPLEQ_ENTRY(BlkdebugRule) active_next; |
93 | } BlkdebugRule; |
94 | |
95 | QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64, |
96 | "BlkdebugIOType mask does not fit into an uint64_t" ); |
97 | |
98 | static QemuOptsList inject_error_opts = { |
99 | .name = "inject-error" , |
100 | .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), |
101 | .desc = { |
102 | { |
103 | .name = "event" , |
104 | .type = QEMU_OPT_STRING, |
105 | }, |
106 | { |
107 | .name = "state" , |
108 | .type = QEMU_OPT_NUMBER, |
109 | }, |
110 | { |
111 | .name = "iotype" , |
112 | .type = QEMU_OPT_STRING, |
113 | }, |
114 | { |
115 | .name = "errno" , |
116 | .type = QEMU_OPT_NUMBER, |
117 | }, |
118 | { |
119 | .name = "sector" , |
120 | .type = QEMU_OPT_NUMBER, |
121 | }, |
122 | { |
123 | .name = "once" , |
124 | .type = QEMU_OPT_BOOL, |
125 | }, |
126 | { |
127 | .name = "immediately" , |
128 | .type = QEMU_OPT_BOOL, |
129 | }, |
130 | { /* end of list */ } |
131 | }, |
132 | }; |
133 | |
134 | static QemuOptsList set_state_opts = { |
135 | .name = "set-state" , |
136 | .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), |
137 | .desc = { |
138 | { |
139 | .name = "event" , |
140 | .type = QEMU_OPT_STRING, |
141 | }, |
142 | { |
143 | .name = "state" , |
144 | .type = QEMU_OPT_NUMBER, |
145 | }, |
146 | { |
147 | .name = "new_state" , |
148 | .type = QEMU_OPT_NUMBER, |
149 | }, |
150 | { /* end of list */ } |
151 | }, |
152 | }; |
153 | |
154 | static QemuOptsList *config_groups[] = { |
155 | &inject_error_opts, |
156 | &set_state_opts, |
157 | NULL |
158 | }; |
159 | |
160 | struct add_rule_data { |
161 | BDRVBlkdebugState *s; |
162 | int action; |
163 | }; |
164 | |
165 | static int add_rule(void *opaque, QemuOpts *opts, Error **errp) |
166 | { |
167 | struct add_rule_data *d = opaque; |
168 | BDRVBlkdebugState *s = d->s; |
169 | const char* event_name; |
170 | int event; |
171 | struct BlkdebugRule *rule; |
172 | int64_t sector; |
173 | BlkdebugIOType iotype; |
174 | Error *local_error = NULL; |
175 | |
176 | /* Find the right event for the rule */ |
177 | event_name = qemu_opt_get(opts, "event" ); |
178 | if (!event_name) { |
179 | error_setg(errp, "Missing event name for rule" ); |
180 | return -1; |
181 | } |
182 | event = qapi_enum_parse(&BlkdebugEvent_lookup, event_name, -1, errp); |
183 | if (event < 0) { |
184 | return -1; |
185 | } |
186 | |
187 | /* Set attributes common for all actions */ |
188 | rule = g_malloc0(sizeof(*rule)); |
189 | *rule = (struct BlkdebugRule) { |
190 | .event = event, |
191 | .action = d->action, |
192 | .state = qemu_opt_get_number(opts, "state" , 0), |
193 | }; |
194 | |
195 | /* Parse action-specific options */ |
196 | switch (d->action) { |
197 | case ACTION_INJECT_ERROR: |
198 | rule->options.inject.error = qemu_opt_get_number(opts, "errno" , EIO); |
199 | rule->options.inject.once = qemu_opt_get_bool(opts, "once" , 0); |
200 | rule->options.inject.immediately = |
201 | qemu_opt_get_bool(opts, "immediately" , 0); |
202 | sector = qemu_opt_get_number(opts, "sector" , -1); |
203 | rule->options.inject.offset = |
204 | sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE; |
205 | |
206 | iotype = qapi_enum_parse(&BlkdebugIOType_lookup, |
207 | qemu_opt_get(opts, "iotype" ), |
208 | BLKDEBUG_IO_TYPE__MAX, &local_error); |
209 | if (local_error) { |
210 | error_propagate(errp, local_error); |
211 | return -1; |
212 | } |
213 | if (iotype != BLKDEBUG_IO_TYPE__MAX) { |
214 | rule->options.inject.iotype_mask = (1ull << iotype); |
215 | } else { |
216 | /* Apply the default */ |
217 | rule->options.inject.iotype_mask = |
218 | (1ull << BLKDEBUG_IO_TYPE_READ) |
219 | | (1ull << BLKDEBUG_IO_TYPE_WRITE) |
220 | | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES) |
221 | | (1ull << BLKDEBUG_IO_TYPE_DISCARD) |
222 | | (1ull << BLKDEBUG_IO_TYPE_FLUSH); |
223 | } |
224 | |
225 | break; |
226 | |
227 | case ACTION_SET_STATE: |
228 | rule->options.set_state.new_state = |
229 | qemu_opt_get_number(opts, "new_state" , 0); |
230 | break; |
231 | |
232 | case ACTION_SUSPEND: |
233 | rule->options.suspend.tag = |
234 | g_strdup(qemu_opt_get(opts, "tag" )); |
235 | break; |
236 | }; |
237 | |
238 | /* Add the rule */ |
239 | QLIST_INSERT_HEAD(&s->rules[event], rule, next); |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | static void remove_rule(BlkdebugRule *rule) |
245 | { |
246 | switch (rule->action) { |
247 | case ACTION_INJECT_ERROR: |
248 | case ACTION_SET_STATE: |
249 | break; |
250 | case ACTION_SUSPEND: |
251 | g_free(rule->options.suspend.tag); |
252 | break; |
253 | } |
254 | |
255 | QLIST_REMOVE(rule, next); |
256 | g_free(rule); |
257 | } |
258 | |
259 | static int read_config(BDRVBlkdebugState *s, const char *filename, |
260 | QDict *options, Error **errp) |
261 | { |
262 | FILE *f = NULL; |
263 | int ret; |
264 | struct add_rule_data d; |
265 | Error *local_err = NULL; |
266 | |
267 | if (filename) { |
268 | f = fopen(filename, "r" ); |
269 | if (f == NULL) { |
270 | error_setg_errno(errp, errno, "Could not read blkdebug config file" ); |
271 | return -errno; |
272 | } |
273 | |
274 | ret = qemu_config_parse(f, config_groups, filename); |
275 | if (ret < 0) { |
276 | error_setg(errp, "Could not parse blkdebug config file" ); |
277 | goto fail; |
278 | } |
279 | } |
280 | |
281 | qemu_config_parse_qdict(options, config_groups, &local_err); |
282 | if (local_err) { |
283 | error_propagate(errp, local_err); |
284 | ret = -EINVAL; |
285 | goto fail; |
286 | } |
287 | |
288 | d.s = s; |
289 | d.action = ACTION_INJECT_ERROR; |
290 | qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err); |
291 | if (local_err) { |
292 | error_propagate(errp, local_err); |
293 | ret = -EINVAL; |
294 | goto fail; |
295 | } |
296 | |
297 | d.action = ACTION_SET_STATE; |
298 | qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err); |
299 | if (local_err) { |
300 | error_propagate(errp, local_err); |
301 | ret = -EINVAL; |
302 | goto fail; |
303 | } |
304 | |
305 | ret = 0; |
306 | fail: |
307 | qemu_opts_reset(&inject_error_opts); |
308 | qemu_opts_reset(&set_state_opts); |
309 | if (f) { |
310 | fclose(f); |
311 | } |
312 | return ret; |
313 | } |
314 | |
315 | /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ |
316 | static void blkdebug_parse_filename(const char *filename, QDict *options, |
317 | Error **errp) |
318 | { |
319 | const char *c; |
320 | |
321 | /* Parse the blkdebug: prefix */ |
322 | if (!strstart(filename, "blkdebug:" , &filename)) { |
323 | /* There was no prefix; therefore, all options have to be already |
324 | present in the QDict (except for the filename) */ |
325 | qdict_put_str(options, "x-image" , filename); |
326 | return; |
327 | } |
328 | |
329 | /* Parse config file path */ |
330 | c = strchr(filename, ':'); |
331 | if (c == NULL) { |
332 | error_setg(errp, "blkdebug requires both config file and image path" ); |
333 | return; |
334 | } |
335 | |
336 | if (c != filename) { |
337 | QString *config_path; |
338 | config_path = qstring_from_substr(filename, 0, c - filename); |
339 | qdict_put(options, "config" , config_path); |
340 | } |
341 | |
342 | /* TODO Allow multi-level nesting and set file.filename here */ |
343 | filename = c + 1; |
344 | qdict_put_str(options, "x-image" , filename); |
345 | } |
346 | |
347 | static QemuOptsList runtime_opts = { |
348 | .name = "blkdebug" , |
349 | .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |
350 | .desc = { |
351 | { |
352 | .name = "config" , |
353 | .type = QEMU_OPT_STRING, |
354 | .help = "Path to the configuration file" , |
355 | }, |
356 | { |
357 | .name = "x-image" , |
358 | .type = QEMU_OPT_STRING, |
359 | .help = "[internal use only, will be removed]" , |
360 | }, |
361 | { |
362 | .name = "align" , |
363 | .type = QEMU_OPT_SIZE, |
364 | .help = "Required alignment in bytes" , |
365 | }, |
366 | { |
367 | .name = "max-transfer" , |
368 | .type = QEMU_OPT_SIZE, |
369 | .help = "Maximum transfer size in bytes" , |
370 | }, |
371 | { |
372 | .name = "opt-write-zero" , |
373 | .type = QEMU_OPT_SIZE, |
374 | .help = "Optimum write zero alignment in bytes" , |
375 | }, |
376 | { |
377 | .name = "max-write-zero" , |
378 | .type = QEMU_OPT_SIZE, |
379 | .help = "Maximum write zero size in bytes" , |
380 | }, |
381 | { |
382 | .name = "opt-discard" , |
383 | .type = QEMU_OPT_SIZE, |
384 | .help = "Optimum discard alignment in bytes" , |
385 | }, |
386 | { |
387 | .name = "max-discard" , |
388 | .type = QEMU_OPT_SIZE, |
389 | .help = "Maximum discard size in bytes" , |
390 | }, |
391 | { /* end of list */ } |
392 | }, |
393 | }; |
394 | |
395 | static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, |
396 | Error **errp) |
397 | { |
398 | BDRVBlkdebugState *s = bs->opaque; |
399 | QemuOpts *opts; |
400 | Error *local_err = NULL; |
401 | int ret; |
402 | uint64_t align; |
403 | |
404 | opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); |
405 | qemu_opts_absorb_qdict(opts, options, &local_err); |
406 | if (local_err) { |
407 | error_propagate(errp, local_err); |
408 | ret = -EINVAL; |
409 | goto out; |
410 | } |
411 | |
412 | /* Read rules from config file or command line options */ |
413 | s->config_file = g_strdup(qemu_opt_get(opts, "config" )); |
414 | ret = read_config(s, s->config_file, options, errp); |
415 | if (ret) { |
416 | goto out; |
417 | } |
418 | |
419 | /* Set initial state */ |
420 | s->state = 1; |
421 | |
422 | /* Open the image file */ |
423 | bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image" ), options, "image" , |
424 | bs, &child_file, false, &local_err); |
425 | if (local_err) { |
426 | ret = -EINVAL; |
427 | error_propagate(errp, local_err); |
428 | goto out; |
429 | } |
430 | |
431 | bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | |
432 | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); |
433 | bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | |
434 | ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & |
435 | bs->file->bs->supported_zero_flags); |
436 | ret = -EINVAL; |
437 | |
438 | /* Set alignment overrides */ |
439 | s->align = qemu_opt_get_size(opts, "align" , 0); |
440 | if (s->align && (s->align >= INT_MAX || !is_power_of_2(s->align))) { |
441 | error_setg(errp, "Cannot meet constraints with align %" PRIu64, |
442 | s->align); |
443 | goto out; |
444 | } |
445 | align = MAX(s->align, bs->file->bs->bl.request_alignment); |
446 | |
447 | s->max_transfer = qemu_opt_get_size(opts, "max-transfer" , 0); |
448 | if (s->max_transfer && |
449 | (s->max_transfer >= INT_MAX || |
450 | !QEMU_IS_ALIGNED(s->max_transfer, align))) { |
451 | error_setg(errp, "Cannot meet constraints with max-transfer %" PRIu64, |
452 | s->max_transfer); |
453 | goto out; |
454 | } |
455 | |
456 | s->opt_write_zero = qemu_opt_get_size(opts, "opt-write-zero" , 0); |
457 | if (s->opt_write_zero && |
458 | (s->opt_write_zero >= INT_MAX || |
459 | !QEMU_IS_ALIGNED(s->opt_write_zero, align))) { |
460 | error_setg(errp, "Cannot meet constraints with opt-write-zero %" PRIu64, |
461 | s->opt_write_zero); |
462 | goto out; |
463 | } |
464 | |
465 | s->max_write_zero = qemu_opt_get_size(opts, "max-write-zero" , 0); |
466 | if (s->max_write_zero && |
467 | (s->max_write_zero >= INT_MAX || |
468 | !QEMU_IS_ALIGNED(s->max_write_zero, |
469 | MAX(s->opt_write_zero, align)))) { |
470 | error_setg(errp, "Cannot meet constraints with max-write-zero %" PRIu64, |
471 | s->max_write_zero); |
472 | goto out; |
473 | } |
474 | |
475 | s->opt_discard = qemu_opt_get_size(opts, "opt-discard" , 0); |
476 | if (s->opt_discard && |
477 | (s->opt_discard >= INT_MAX || |
478 | !QEMU_IS_ALIGNED(s->opt_discard, align))) { |
479 | error_setg(errp, "Cannot meet constraints with opt-discard %" PRIu64, |
480 | s->opt_discard); |
481 | goto out; |
482 | } |
483 | |
484 | s->max_discard = qemu_opt_get_size(opts, "max-discard" , 0); |
485 | if (s->max_discard && |
486 | (s->max_discard >= INT_MAX || |
487 | !QEMU_IS_ALIGNED(s->max_discard, |
488 | MAX(s->opt_discard, align)))) { |
489 | error_setg(errp, "Cannot meet constraints with max-discard %" PRIu64, |
490 | s->max_discard); |
491 | goto out; |
492 | } |
493 | |
494 | bdrv_debug_event(bs, BLKDBG_NONE); |
495 | |
496 | ret = 0; |
497 | out: |
498 | if (ret < 0) { |
499 | g_free(s->config_file); |
500 | } |
501 | qemu_opts_del(opts); |
502 | return ret; |
503 | } |
504 | |
505 | static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
506 | BlkdebugIOType iotype) |
507 | { |
508 | BDRVBlkdebugState *s = bs->opaque; |
509 | BlkdebugRule *rule = NULL; |
510 | int error; |
511 | bool immediately; |
512 | |
513 | QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { |
514 | uint64_t inject_offset = rule->options.inject.offset; |
515 | |
516 | if ((inject_offset == -1 || |
517 | (bytes && inject_offset >= offset && |
518 | inject_offset < offset + bytes)) && |
519 | (rule->options.inject.iotype_mask & (1ull << iotype))) |
520 | { |
521 | break; |
522 | } |
523 | } |
524 | |
525 | if (!rule || !rule->options.inject.error) { |
526 | return 0; |
527 | } |
528 | |
529 | immediately = rule->options.inject.immediately; |
530 | error = rule->options.inject.error; |
531 | |
532 | if (rule->options.inject.once) { |
533 | QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); |
534 | remove_rule(rule); |
535 | } |
536 | |
537 | if (!immediately) { |
538 | aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self()); |
539 | qemu_coroutine_yield(); |
540 | } |
541 | |
542 | return -error; |
543 | } |
544 | |
545 | static int coroutine_fn |
546 | blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
547 | QEMUIOVector *qiov, int flags) |
548 | { |
549 | int err; |
550 | |
551 | /* Sanity check block layer guarantees */ |
552 | assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); |
553 | assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); |
554 | if (bs->bl.max_transfer) { |
555 | assert(bytes <= bs->bl.max_transfer); |
556 | } |
557 | |
558 | err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ); |
559 | if (err) { |
560 | return err; |
561 | } |
562 | |
563 | return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); |
564 | } |
565 | |
566 | static int coroutine_fn |
567 | blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
568 | QEMUIOVector *qiov, int flags) |
569 | { |
570 | int err; |
571 | |
572 | /* Sanity check block layer guarantees */ |
573 | assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); |
574 | assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); |
575 | if (bs->bl.max_transfer) { |
576 | assert(bytes <= bs->bl.max_transfer); |
577 | } |
578 | |
579 | err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE); |
580 | if (err) { |
581 | return err; |
582 | } |
583 | |
584 | return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); |
585 | } |
586 | |
587 | static int blkdebug_co_flush(BlockDriverState *bs) |
588 | { |
589 | int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH); |
590 | |
591 | if (err) { |
592 | return err; |
593 | } |
594 | |
595 | return bdrv_co_flush(bs->file->bs); |
596 | } |
597 | |
598 | static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs, |
599 | int64_t offset, int bytes, |
600 | BdrvRequestFlags flags) |
601 | { |
602 | uint32_t align = MAX(bs->bl.request_alignment, |
603 | bs->bl.pwrite_zeroes_alignment); |
604 | int err; |
605 | |
606 | /* Only pass through requests that are larger than requested |
607 | * preferred alignment (so that we test the fallback to writes on |
608 | * unaligned portions), and check that the block layer never hands |
609 | * us anything unaligned that crosses an alignment boundary. */ |
610 | if (bytes < align) { |
611 | assert(QEMU_IS_ALIGNED(offset, align) || |
612 | QEMU_IS_ALIGNED(offset + bytes, align) || |
613 | DIV_ROUND_UP(offset, align) == |
614 | DIV_ROUND_UP(offset + bytes, align)); |
615 | return -ENOTSUP; |
616 | } |
617 | assert(QEMU_IS_ALIGNED(offset, align)); |
618 | assert(QEMU_IS_ALIGNED(bytes, align)); |
619 | if (bs->bl.max_pwrite_zeroes) { |
620 | assert(bytes <= bs->bl.max_pwrite_zeroes); |
621 | } |
622 | |
623 | err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES); |
624 | if (err) { |
625 | return err; |
626 | } |
627 | |
628 | return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); |
629 | } |
630 | |
631 | static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, |
632 | int64_t offset, int bytes) |
633 | { |
634 | uint32_t align = bs->bl.pdiscard_alignment; |
635 | int err; |
636 | |
637 | /* Only pass through requests that are larger than requested |
638 | * minimum alignment, and ensure that unaligned requests do not |
639 | * cross optimum discard boundaries. */ |
640 | if (bytes < bs->bl.request_alignment) { |
641 | assert(QEMU_IS_ALIGNED(offset, align) || |
642 | QEMU_IS_ALIGNED(offset + bytes, align) || |
643 | DIV_ROUND_UP(offset, align) == |
644 | DIV_ROUND_UP(offset + bytes, align)); |
645 | return -ENOTSUP; |
646 | } |
647 | assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); |
648 | assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); |
649 | if (align && bytes >= align) { |
650 | assert(QEMU_IS_ALIGNED(offset, align)); |
651 | assert(QEMU_IS_ALIGNED(bytes, align)); |
652 | } |
653 | if (bs->bl.max_pdiscard) { |
654 | assert(bytes <= bs->bl.max_pdiscard); |
655 | } |
656 | |
657 | err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD); |
658 | if (err) { |
659 | return err; |
660 | } |
661 | |
662 | return bdrv_co_pdiscard(bs->file, offset, bytes); |
663 | } |
664 | |
665 | static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, |
666 | bool want_zero, |
667 | int64_t offset, |
668 | int64_t bytes, |
669 | int64_t *pnum, |
670 | int64_t *map, |
671 | BlockDriverState **file) |
672 | { |
673 | int err; |
674 | |
675 | assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment)); |
676 | |
677 | err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS); |
678 | if (err) { |
679 | return err; |
680 | } |
681 | |
682 | return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes, |
683 | pnum, map, file); |
684 | } |
685 | |
686 | static void blkdebug_close(BlockDriverState *bs) |
687 | { |
688 | BDRVBlkdebugState *s = bs->opaque; |
689 | BlkdebugRule *rule, *next; |
690 | int i; |
691 | |
692 | for (i = 0; i < BLKDBG__MAX; i++) { |
693 | QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { |
694 | remove_rule(rule); |
695 | } |
696 | } |
697 | |
698 | g_free(s->config_file); |
699 | } |
700 | |
701 | static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) |
702 | { |
703 | BDRVBlkdebugState *s = bs->opaque; |
704 | BlkdebugSuspendedReq r; |
705 | |
706 | r = (BlkdebugSuspendedReq) { |
707 | .co = qemu_coroutine_self(), |
708 | .tag = g_strdup(rule->options.suspend.tag), |
709 | }; |
710 | |
711 | remove_rule(rule); |
712 | QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); |
713 | |
714 | if (!qtest_enabled()) { |
715 | printf("blkdebug: Suspended request '%s'\n" , r.tag); |
716 | } |
717 | qemu_coroutine_yield(); |
718 | if (!qtest_enabled()) { |
719 | printf("blkdebug: Resuming request '%s'\n" , r.tag); |
720 | } |
721 | |
722 | QLIST_REMOVE(&r, next); |
723 | g_free(r.tag); |
724 | } |
725 | |
726 | static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, |
727 | bool injected) |
728 | { |
729 | BDRVBlkdebugState *s = bs->opaque; |
730 | |
731 | /* Only process rules for the current state */ |
732 | if (rule->state && rule->state != s->state) { |
733 | return injected; |
734 | } |
735 | |
736 | /* Take the action */ |
737 | switch (rule->action) { |
738 | case ACTION_INJECT_ERROR: |
739 | if (!injected) { |
740 | QSIMPLEQ_INIT(&s->active_rules); |
741 | injected = true; |
742 | } |
743 | QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next); |
744 | break; |
745 | |
746 | case ACTION_SET_STATE: |
747 | s->new_state = rule->options.set_state.new_state; |
748 | break; |
749 | |
750 | case ACTION_SUSPEND: |
751 | suspend_request(bs, rule); |
752 | break; |
753 | } |
754 | return injected; |
755 | } |
756 | |
757 | static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) |
758 | { |
759 | BDRVBlkdebugState *s = bs->opaque; |
760 | struct BlkdebugRule *rule, *next; |
761 | bool injected; |
762 | |
763 | assert((int)event >= 0 && event < BLKDBG__MAX); |
764 | |
765 | injected = false; |
766 | s->new_state = s->state; |
767 | QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) { |
768 | injected = process_rule(bs, rule, injected); |
769 | } |
770 | s->state = s->new_state; |
771 | } |
772 | |
773 | static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, |
774 | const char *tag) |
775 | { |
776 | BDRVBlkdebugState *s = bs->opaque; |
777 | struct BlkdebugRule *rule; |
778 | int blkdebug_event; |
779 | |
780 | blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL); |
781 | if (blkdebug_event < 0) { |
782 | return -ENOENT; |
783 | } |
784 | |
785 | rule = g_malloc(sizeof(*rule)); |
786 | *rule = (struct BlkdebugRule) { |
787 | .event = blkdebug_event, |
788 | .action = ACTION_SUSPEND, |
789 | .state = 0, |
790 | .options.suspend.tag = g_strdup(tag), |
791 | }; |
792 | |
793 | QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next); |
794 | |
795 | return 0; |
796 | } |
797 | |
798 | static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag) |
799 | { |
800 | BDRVBlkdebugState *s = bs->opaque; |
801 | BlkdebugSuspendedReq *r, *next; |
802 | |
803 | QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) { |
804 | if (!strcmp(r->tag, tag)) { |
805 | qemu_coroutine_enter(r->co); |
806 | return 0; |
807 | } |
808 | } |
809 | return -ENOENT; |
810 | } |
811 | |
812 | static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, |
813 | const char *tag) |
814 | { |
815 | BDRVBlkdebugState *s = bs->opaque; |
816 | BlkdebugSuspendedReq *r, *r_next; |
817 | BlkdebugRule *rule, *next; |
818 | int i, ret = -ENOENT; |
819 | |
820 | for (i = 0; i < BLKDBG__MAX; i++) { |
821 | QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { |
822 | if (rule->action == ACTION_SUSPEND && |
823 | !strcmp(rule->options.suspend.tag, tag)) { |
824 | remove_rule(rule); |
825 | ret = 0; |
826 | } |
827 | } |
828 | } |
829 | QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) { |
830 | if (!strcmp(r->tag, tag)) { |
831 | qemu_coroutine_enter(r->co); |
832 | ret = 0; |
833 | } |
834 | } |
835 | return ret; |
836 | } |
837 | |
838 | static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) |
839 | { |
840 | BDRVBlkdebugState *s = bs->opaque; |
841 | BlkdebugSuspendedReq *r; |
842 | |
843 | QLIST_FOREACH(r, &s->suspended_reqs, next) { |
844 | if (!strcmp(r->tag, tag)) { |
845 | return true; |
846 | } |
847 | } |
848 | return false; |
849 | } |
850 | |
851 | static int64_t blkdebug_getlength(BlockDriverState *bs) |
852 | { |
853 | return bdrv_getlength(bs->file->bs); |
854 | } |
855 | |
856 | static void blkdebug_refresh_filename(BlockDriverState *bs) |
857 | { |
858 | BDRVBlkdebugState *s = bs->opaque; |
859 | const QDictEntry *e; |
860 | int ret; |
861 | |
862 | if (!bs->file->bs->exact_filename[0]) { |
863 | return; |
864 | } |
865 | |
866 | for (e = qdict_first(bs->full_open_options); e; |
867 | e = qdict_next(bs->full_open_options, e)) |
868 | { |
869 | /* Real child options are under "image", but "x-image" may |
870 | * contain a filename */ |
871 | if (strcmp(qdict_entry_key(e), "config" ) && |
872 | strcmp(qdict_entry_key(e), "image" ) && |
873 | strcmp(qdict_entry_key(e), "x-image" ) && |
874 | strcmp(qdict_entry_key(e), "driver" )) |
875 | { |
876 | return; |
877 | } |
878 | } |
879 | |
880 | ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), |
881 | "blkdebug:%s:%s" , |
882 | s->config_file ?: "" , bs->file->bs->exact_filename); |
883 | if (ret >= sizeof(bs->exact_filename)) { |
884 | /* An overflow makes the filename unusable, so do not report any */ |
885 | bs->exact_filename[0] = 0; |
886 | } |
887 | } |
888 | |
889 | static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) |
890 | { |
891 | BDRVBlkdebugState *s = bs->opaque; |
892 | |
893 | if (s->align) { |
894 | bs->bl.request_alignment = s->align; |
895 | } |
896 | if (s->max_transfer) { |
897 | bs->bl.max_transfer = s->max_transfer; |
898 | } |
899 | if (s->opt_write_zero) { |
900 | bs->bl.pwrite_zeroes_alignment = s->opt_write_zero; |
901 | } |
902 | if (s->max_write_zero) { |
903 | bs->bl.max_pwrite_zeroes = s->max_write_zero; |
904 | } |
905 | if (s->opt_discard) { |
906 | bs->bl.pdiscard_alignment = s->opt_discard; |
907 | } |
908 | if (s->max_discard) { |
909 | bs->bl.max_pdiscard = s->max_discard; |
910 | } |
911 | } |
912 | |
913 | static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, |
914 | BlockReopenQueue *queue, Error **errp) |
915 | { |
916 | return 0; |
917 | } |
918 | |
919 | static const char *const blkdebug_strong_runtime_opts[] = { |
920 | "config" , |
921 | "inject-error." , |
922 | "set-state." , |
923 | "align" , |
924 | "max-transfer" , |
925 | "opt-write-zero" , |
926 | "max-write-zero" , |
927 | "opt-discard" , |
928 | "max-discard" , |
929 | |
930 | NULL |
931 | }; |
932 | |
933 | static BlockDriver bdrv_blkdebug = { |
934 | .format_name = "blkdebug" , |
935 | .protocol_name = "blkdebug" , |
936 | .instance_size = sizeof(BDRVBlkdebugState), |
937 | .is_filter = true, |
938 | |
939 | .bdrv_parse_filename = blkdebug_parse_filename, |
940 | .bdrv_file_open = blkdebug_open, |
941 | .bdrv_close = blkdebug_close, |
942 | .bdrv_reopen_prepare = blkdebug_reopen_prepare, |
943 | .bdrv_child_perm = bdrv_filter_default_perms, |
944 | |
945 | .bdrv_getlength = blkdebug_getlength, |
946 | .bdrv_refresh_filename = blkdebug_refresh_filename, |
947 | .bdrv_refresh_limits = blkdebug_refresh_limits, |
948 | |
949 | .bdrv_co_preadv = blkdebug_co_preadv, |
950 | .bdrv_co_pwritev = blkdebug_co_pwritev, |
951 | .bdrv_co_flush_to_disk = blkdebug_co_flush, |
952 | .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes, |
953 | .bdrv_co_pdiscard = blkdebug_co_pdiscard, |
954 | .bdrv_co_block_status = blkdebug_co_block_status, |
955 | |
956 | .bdrv_debug_event = blkdebug_debug_event, |
957 | .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, |
958 | .bdrv_debug_remove_breakpoint |
959 | = blkdebug_debug_remove_breakpoint, |
960 | .bdrv_debug_resume = blkdebug_debug_resume, |
961 | .bdrv_debug_is_suspended = blkdebug_debug_is_suspended, |
962 | |
963 | .strong_runtime_opts = blkdebug_strong_runtime_opts, |
964 | }; |
965 | |
966 | static void bdrv_blkdebug_init(void) |
967 | { |
968 | bdrv_register(&bdrv_blkdebug); |
969 | } |
970 | |
971 | block_init(bdrv_blkdebug_init); |
972 | |