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