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/osdep.h" 26 #include "qapi/error.h" 27 #include "qemu/cutils.h" 28 #include "qemu/config-file.h" 29 #include "block/block_int.h" 30 #include "qemu/module.h" 31 #include "qapi/qmp/qbool.h" 32 #include "qapi/qmp/qdict.h" 33 #include "qapi/qmp/qint.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 41 QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX]; 42 QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; 43 QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs; 44 } BDRVBlkdebugState; 45 46 typedef struct BlkdebugAIOCB { 47 BlockAIOCB common; 48 QEMUBH *bh; 49 int ret; 50 } BlkdebugAIOCB; 51 52 typedef struct BlkdebugSuspendedReq { 53 Coroutine *co; 54 char *tag; 55 QLIST_ENTRY(BlkdebugSuspendedReq) next; 56 } BlkdebugSuspendedReq; 57 58 static const AIOCBInfo blkdebug_aiocb_info = { 59 .aiocb_size = sizeof(BlkdebugAIOCB), 60 }; 61 62 enum { 63 ACTION_INJECT_ERROR, 64 ACTION_SET_STATE, 65 ACTION_SUSPEND, 66 }; 67 68 typedef struct BlkdebugRule { 69 BlkdebugEvent event; 70 int action; 71 int state; 72 union { 73 struct { 74 int error; 75 int immediately; 76 int once; 77 int64_t sector; 78 } inject; 79 struct { 80 int new_state; 81 } set_state; 82 struct { 83 char *tag; 84 } suspend; 85 } options; 86 QLIST_ENTRY(BlkdebugRule) next; 87 QSIMPLEQ_ENTRY(BlkdebugRule) active_next; 88 } BlkdebugRule; 89 90 static QemuOptsList inject_error_opts = { 91 .name = "inject-error", 92 .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), 93 .desc = { 94 { 95 .name = "event", 96 .type = QEMU_OPT_STRING, 97 }, 98 { 99 .name = "state", 100 .type = QEMU_OPT_NUMBER, 101 }, 102 { 103 .name = "errno", 104 .type = QEMU_OPT_NUMBER, 105 }, 106 { 107 .name = "sector", 108 .type = QEMU_OPT_NUMBER, 109 }, 110 { 111 .name = "once", 112 .type = QEMU_OPT_BOOL, 113 }, 114 { 115 .name = "immediately", 116 .type = QEMU_OPT_BOOL, 117 }, 118 { /* end of list */ } 119 }, 120 }; 121 122 static QemuOptsList set_state_opts = { 123 .name = "set-state", 124 .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), 125 .desc = { 126 { 127 .name = "event", 128 .type = QEMU_OPT_STRING, 129 }, 130 { 131 .name = "state", 132 .type = QEMU_OPT_NUMBER, 133 }, 134 { 135 .name = "new_state", 136 .type = QEMU_OPT_NUMBER, 137 }, 138 { /* end of list */ } 139 }, 140 }; 141 142 static QemuOptsList *config_groups[] = { 143 &inject_error_opts, 144 &set_state_opts, 145 NULL 146 }; 147 148 static int get_event_by_name(const char *name, BlkdebugEvent *event) 149 { 150 int i; 151 152 for (i = 0; i < BLKDBG__MAX; i++) { 153 if (!strcmp(BlkdebugEvent_lookup[i], name)) { 154 *event = i; 155 return 0; 156 } 157 } 158 159 return -1; 160 } 161 162 struct add_rule_data { 163 BDRVBlkdebugState *s; 164 int action; 165 }; 166 167 static int add_rule(void *opaque, QemuOpts *opts, Error **errp) 168 { 169 struct add_rule_data *d = opaque; 170 BDRVBlkdebugState *s = d->s; 171 const char* event_name; 172 BlkdebugEvent event; 173 struct BlkdebugRule *rule; 174 175 /* Find the right event for the rule */ 176 event_name = qemu_opt_get(opts, "event"); 177 if (!event_name) { 178 error_setg(errp, "Missing event name for rule"); 179 return -1; 180 } else if (get_event_by_name(event_name, &event) < 0) { 181 error_setg(errp, "Invalid event name \"%s\"", event_name); 182 return -1; 183 } 184 185 /* Set attributes common for all actions */ 186 rule = g_malloc0(sizeof(*rule)); 187 *rule = (struct BlkdebugRule) { 188 .event = event, 189 .action = d->action, 190 .state = qemu_opt_get_number(opts, "state", 0), 191 }; 192 193 /* Parse action-specific options */ 194 switch (d->action) { 195 case ACTION_INJECT_ERROR: 196 rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); 197 rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); 198 rule->options.inject.immediately = 199 qemu_opt_get_bool(opts, "immediately", 0); 200 rule->options.inject.sector = qemu_opt_get_number(opts, "sector", -1); 201 break; 202 203 case ACTION_SET_STATE: 204 rule->options.set_state.new_state = 205 qemu_opt_get_number(opts, "new_state", 0); 206 break; 207 208 case ACTION_SUSPEND: 209 rule->options.suspend.tag = 210 g_strdup(qemu_opt_get(opts, "tag")); 211 break; 212 }; 213 214 /* Add the rule */ 215 QLIST_INSERT_HEAD(&s->rules[event], rule, next); 216 217 return 0; 218 } 219 220 static void remove_rule(BlkdebugRule *rule) 221 { 222 switch (rule->action) { 223 case ACTION_INJECT_ERROR: 224 case ACTION_SET_STATE: 225 break; 226 case ACTION_SUSPEND: 227 g_free(rule->options.suspend.tag); 228 break; 229 } 230 231 QLIST_REMOVE(rule, next); 232 g_free(rule); 233 } 234 235 static int read_config(BDRVBlkdebugState *s, const char *filename, 236 QDict *options, Error **errp) 237 { 238 FILE *f = NULL; 239 int ret; 240 struct add_rule_data d; 241 Error *local_err = NULL; 242 243 if (filename) { 244 f = fopen(filename, "r"); 245 if (f == NULL) { 246 error_setg_errno(errp, errno, "Could not read blkdebug config file"); 247 return -errno; 248 } 249 250 ret = qemu_config_parse(f, config_groups, filename); 251 if (ret < 0) { 252 error_setg(errp, "Could not parse blkdebug config file"); 253 ret = -EINVAL; 254 goto fail; 255 } 256 } 257 258 qemu_config_parse_qdict(options, config_groups, &local_err); 259 if (local_err) { 260 error_propagate(errp, local_err); 261 ret = -EINVAL; 262 goto fail; 263 } 264 265 d.s = s; 266 d.action = ACTION_INJECT_ERROR; 267 qemu_opts_foreach(&inject_error_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 d.action = ACTION_SET_STATE; 275 qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err); 276 if (local_err) { 277 error_propagate(errp, local_err); 278 ret = -EINVAL; 279 goto fail; 280 } 281 282 ret = 0; 283 fail: 284 qemu_opts_reset(&inject_error_opts); 285 qemu_opts_reset(&set_state_opts); 286 if (f) { 287 fclose(f); 288 } 289 return ret; 290 } 291 292 /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ 293 static void blkdebug_parse_filename(const char *filename, QDict *options, 294 Error **errp) 295 { 296 const char *c; 297 298 /* Parse the blkdebug: prefix */ 299 if (!strstart(filename, "blkdebug:", &filename)) { 300 /* There was no prefix; therefore, all options have to be already 301 present in the QDict (except for the filename) */ 302 qdict_put(options, "x-image", qstring_from_str(filename)); 303 return; 304 } 305 306 /* Parse config file path */ 307 c = strchr(filename, ':'); 308 if (c == NULL) { 309 error_setg(errp, "blkdebug requires both config file and image path"); 310 return; 311 } 312 313 if (c != filename) { 314 QString *config_path; 315 config_path = qstring_from_substr(filename, 0, c - filename - 1); 316 qdict_put(options, "config", config_path); 317 } 318 319 /* TODO Allow multi-level nesting and set file.filename here */ 320 filename = c + 1; 321 qdict_put(options, "x-image", qstring_from_str(filename)); 322 } 323 324 static QemuOptsList runtime_opts = { 325 .name = "blkdebug", 326 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 327 .desc = { 328 { 329 .name = "config", 330 .type = QEMU_OPT_STRING, 331 .help = "Path to the configuration file", 332 }, 333 { 334 .name = "x-image", 335 .type = QEMU_OPT_STRING, 336 .help = "[internal use only, will be removed]", 337 }, 338 { 339 .name = "align", 340 .type = QEMU_OPT_SIZE, 341 .help = "Required alignment in bytes", 342 }, 343 { /* end of list */ } 344 }, 345 }; 346 347 static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, 348 Error **errp) 349 { 350 BDRVBlkdebugState *s = bs->opaque; 351 QemuOpts *opts; 352 Error *local_err = NULL; 353 const char *config; 354 uint64_t align; 355 int ret; 356 357 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 358 qemu_opts_absorb_qdict(opts, options, &local_err); 359 if (local_err) { 360 error_propagate(errp, local_err); 361 ret = -EINVAL; 362 goto out; 363 } 364 365 /* Read rules from config file or command line options */ 366 config = qemu_opt_get(opts, "config"); 367 ret = read_config(s, config, options, errp); 368 if (ret) { 369 goto out; 370 } 371 372 /* Set initial state */ 373 s->state = 1; 374 375 /* Open the image file */ 376 bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", 377 bs, &child_file, false, &local_err); 378 if (local_err) { 379 ret = -EINVAL; 380 error_propagate(errp, local_err); 381 goto out; 382 } 383 384 /* Set request alignment */ 385 align = qemu_opt_get_size(opts, "align", bs->request_alignment); 386 if (align > 0 && align < INT_MAX && !(align & (align - 1))) { 387 bs->request_alignment = align; 388 } else { 389 error_setg(errp, "Invalid alignment"); 390 ret = -EINVAL; 391 goto fail_unref; 392 } 393 394 ret = 0; 395 goto out; 396 397 fail_unref: 398 bdrv_unref_child(bs, bs->file); 399 out: 400 qemu_opts_del(opts); 401 return ret; 402 } 403 404 static void error_callback_bh(void *opaque) 405 { 406 struct BlkdebugAIOCB *acb = opaque; 407 qemu_bh_delete(acb->bh); 408 acb->common.cb(acb->common.opaque, acb->ret); 409 qemu_aio_unref(acb); 410 } 411 412 static BlockAIOCB *inject_error(BlockDriverState *bs, 413 BlockCompletionFunc *cb, void *opaque, BlkdebugRule *rule) 414 { 415 BDRVBlkdebugState *s = bs->opaque; 416 int error = rule->options.inject.error; 417 struct BlkdebugAIOCB *acb; 418 QEMUBH *bh; 419 bool immediately = rule->options.inject.immediately; 420 421 if (rule->options.inject.once) { 422 QSIMPLEQ_REMOVE(&s->active_rules, rule, BlkdebugRule, active_next); 423 remove_rule(rule); 424 } 425 426 if (immediately) { 427 return NULL; 428 } 429 430 acb = qemu_aio_get(&blkdebug_aiocb_info, bs, cb, opaque); 431 acb->ret = -error; 432 433 bh = aio_bh_new(bdrv_get_aio_context(bs), error_callback_bh, acb); 434 acb->bh = bh; 435 qemu_bh_schedule(bh); 436 437 return &acb->common; 438 } 439 440 static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, 441 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, 442 BlockCompletionFunc *cb, void *opaque) 443 { 444 BDRVBlkdebugState *s = bs->opaque; 445 BlkdebugRule *rule = NULL; 446 447 QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { 448 if (rule->options.inject.sector == -1 || 449 (rule->options.inject.sector >= sector_num && 450 rule->options.inject.sector < sector_num + nb_sectors)) { 451 break; 452 } 453 } 454 455 if (rule && rule->options.inject.error) { 456 return inject_error(bs, cb, opaque, rule); 457 } 458 459 return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors, 460 cb, opaque); 461 } 462 463 static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, 464 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, 465 BlockCompletionFunc *cb, void *opaque) 466 { 467 BDRVBlkdebugState *s = bs->opaque; 468 BlkdebugRule *rule = NULL; 469 470 QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { 471 if (rule->options.inject.sector == -1 || 472 (rule->options.inject.sector >= sector_num && 473 rule->options.inject.sector < sector_num + nb_sectors)) { 474 break; 475 } 476 } 477 478 if (rule && rule->options.inject.error) { 479 return inject_error(bs, cb, opaque, rule); 480 } 481 482 return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, 483 cb, opaque); 484 } 485 486 static BlockAIOCB *blkdebug_aio_flush(BlockDriverState *bs, 487 BlockCompletionFunc *cb, void *opaque) 488 { 489 BDRVBlkdebugState *s = bs->opaque; 490 BlkdebugRule *rule = NULL; 491 492 QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) { 493 if (rule->options.inject.sector == -1) { 494 break; 495 } 496 } 497 498 if (rule && rule->options.inject.error) { 499 return inject_error(bs, cb, opaque, rule); 500 } 501 502 return bdrv_aio_flush(bs->file->bs, cb, opaque); 503 } 504 505 506 static void blkdebug_close(BlockDriverState *bs) 507 { 508 BDRVBlkdebugState *s = bs->opaque; 509 BlkdebugRule *rule, *next; 510 int i; 511 512 for (i = 0; i < BLKDBG__MAX; i++) { 513 QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { 514 remove_rule(rule); 515 } 516 } 517 } 518 519 static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule) 520 { 521 BDRVBlkdebugState *s = bs->opaque; 522 BlkdebugSuspendedReq r; 523 524 r = (BlkdebugSuspendedReq) { 525 .co = qemu_coroutine_self(), 526 .tag = g_strdup(rule->options.suspend.tag), 527 }; 528 529 remove_rule(rule); 530 QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next); 531 532 if (!qtest_enabled()) { 533 printf("blkdebug: Suspended request '%s'\n", r.tag); 534 } 535 qemu_coroutine_yield(); 536 if (!qtest_enabled()) { 537 printf("blkdebug: Resuming request '%s'\n", r.tag); 538 } 539 540 QLIST_REMOVE(&r, next); 541 g_free(r.tag); 542 } 543 544 static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, 545 bool injected) 546 { 547 BDRVBlkdebugState *s = bs->opaque; 548 549 /* Only process rules for the current state */ 550 if (rule->state && rule->state != s->state) { 551 return injected; 552 } 553 554 /* Take the action */ 555 switch (rule->action) { 556 case ACTION_INJECT_ERROR: 557 if (!injected) { 558 QSIMPLEQ_INIT(&s->active_rules); 559 injected = true; 560 } 561 QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next); 562 break; 563 564 case ACTION_SET_STATE: 565 s->new_state = rule->options.set_state.new_state; 566 break; 567 568 case ACTION_SUSPEND: 569 suspend_request(bs, rule); 570 break; 571 } 572 return injected; 573 } 574 575 static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event) 576 { 577 BDRVBlkdebugState *s = bs->opaque; 578 struct BlkdebugRule *rule, *next; 579 bool injected; 580 581 assert((int)event >= 0 && event < BLKDBG__MAX); 582 583 injected = false; 584 s->new_state = s->state; 585 QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) { 586 injected = process_rule(bs, rule, injected); 587 } 588 s->state = s->new_state; 589 } 590 591 static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event, 592 const char *tag) 593 { 594 BDRVBlkdebugState *s = bs->opaque; 595 struct BlkdebugRule *rule; 596 BlkdebugEvent blkdebug_event; 597 598 if (get_event_by_name(event, &blkdebug_event) < 0) { 599 return -ENOENT; 600 } 601 602 603 rule = g_malloc(sizeof(*rule)); 604 *rule = (struct BlkdebugRule) { 605 .event = blkdebug_event, 606 .action = ACTION_SUSPEND, 607 .state = 0, 608 .options.suspend.tag = g_strdup(tag), 609 }; 610 611 QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next); 612 613 return 0; 614 } 615 616 static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag) 617 { 618 BDRVBlkdebugState *s = bs->opaque; 619 BlkdebugSuspendedReq *r, *next; 620 621 QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) { 622 if (!strcmp(r->tag, tag)) { 623 qemu_coroutine_enter(r->co, NULL); 624 return 0; 625 } 626 } 627 return -ENOENT; 628 } 629 630 static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs, 631 const char *tag) 632 { 633 BDRVBlkdebugState *s = bs->opaque; 634 BlkdebugSuspendedReq *r, *r_next; 635 BlkdebugRule *rule, *next; 636 int i, ret = -ENOENT; 637 638 for (i = 0; i < BLKDBG__MAX; i++) { 639 QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { 640 if (rule->action == ACTION_SUSPEND && 641 !strcmp(rule->options.suspend.tag, tag)) { 642 remove_rule(rule); 643 ret = 0; 644 } 645 } 646 } 647 QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) { 648 if (!strcmp(r->tag, tag)) { 649 qemu_coroutine_enter(r->co, NULL); 650 ret = 0; 651 } 652 } 653 return ret; 654 } 655 656 static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag) 657 { 658 BDRVBlkdebugState *s = bs->opaque; 659 BlkdebugSuspendedReq *r; 660 661 QLIST_FOREACH(r, &s->suspended_reqs, next) { 662 if (!strcmp(r->tag, tag)) { 663 return true; 664 } 665 } 666 return false; 667 } 668 669 static int64_t blkdebug_getlength(BlockDriverState *bs) 670 { 671 return bdrv_getlength(bs->file->bs); 672 } 673 674 static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) 675 { 676 return bdrv_truncate(bs->file->bs, offset); 677 } 678 679 static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) 680 { 681 QDict *opts; 682 const QDictEntry *e; 683 bool force_json = false; 684 685 for (e = qdict_first(options); e; e = qdict_next(options, e)) { 686 if (strcmp(qdict_entry_key(e), "config") && 687 strcmp(qdict_entry_key(e), "x-image")) 688 { 689 force_json = true; 690 break; 691 } 692 } 693 694 if (force_json && !bs->file->bs->full_open_options) { 695 /* The config file cannot be recreated, so creating a plain filename 696 * is impossible */ 697 return; 698 } 699 700 if (!force_json && bs->file->bs->exact_filename[0]) { 701 snprintf(bs->exact_filename, sizeof(bs->exact_filename), 702 "blkdebug:%s:%s", 703 qdict_get_try_str(options, "config") ?: "", 704 bs->file->bs->exact_filename); 705 } 706 707 opts = qdict_new(); 708 qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("blkdebug"))); 709 710 QINCREF(bs->file->bs->full_open_options); 711 qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options)); 712 713 for (e = qdict_first(options); e; e = qdict_next(options, e)) { 714 if (strcmp(qdict_entry_key(e), "x-image")) { 715 qobject_incref(qdict_entry_value(e)); 716 qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); 717 } 718 } 719 720 bs->full_open_options = opts; 721 } 722 723 static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, 724 BlockReopenQueue *queue, Error **errp) 725 { 726 return 0; 727 } 728 729 static BlockDriver bdrv_blkdebug = { 730 .format_name = "blkdebug", 731 .protocol_name = "blkdebug", 732 .instance_size = sizeof(BDRVBlkdebugState), 733 734 .bdrv_parse_filename = blkdebug_parse_filename, 735 .bdrv_file_open = blkdebug_open, 736 .bdrv_close = blkdebug_close, 737 .bdrv_reopen_prepare = blkdebug_reopen_prepare, 738 .bdrv_getlength = blkdebug_getlength, 739 .bdrv_truncate = blkdebug_truncate, 740 .bdrv_refresh_filename = blkdebug_refresh_filename, 741 742 .bdrv_aio_readv = blkdebug_aio_readv, 743 .bdrv_aio_writev = blkdebug_aio_writev, 744 .bdrv_aio_flush = blkdebug_aio_flush, 745 746 .bdrv_debug_event = blkdebug_debug_event, 747 .bdrv_debug_breakpoint = blkdebug_debug_breakpoint, 748 .bdrv_debug_remove_breakpoint 749 = blkdebug_debug_remove_breakpoint, 750 .bdrv_debug_resume = blkdebug_debug_resume, 751 .bdrv_debug_is_suspended = blkdebug_debug_is_suspended, 752 }; 753 754 static void bdrv_blkdebug_init(void) 755 { 756 bdrv_register(&bdrv_blkdebug); 757 } 758 759 block_init(bdrv_blkdebug_init); 760