|
|
1.1 ! root 1: /* ! 2: * Block protocol for I/O error injection ! 3: * ! 4: * Copyright (c) 2010 Kevin Wolf <[email protected]> ! 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" ! 26: #include "block_int.h" ! 27: #include "module.h" ! 28: ! 29: typedef struct BlkdebugVars { ! 30: int state; ! 31: ! 32: /* If inject_errno != 0, an error is injected for requests */ ! 33: int inject_errno; ! 34: ! 35: /* Decides if all future requests fail (false) or only the next one and ! 36: * after the next request inject_errno is reset to 0 (true) */ ! 37: bool inject_once; ! 38: ! 39: /* Decides if aio_readv/writev fails right away (true) or returns an error ! 40: * return value only in the callback (false) */ ! 41: bool inject_immediately; ! 42: } BlkdebugVars; ! 43: ! 44: typedef struct BDRVBlkdebugState { ! 45: BlkdebugVars vars; ! 46: QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; ! 47: } BDRVBlkdebugState; ! 48: ! 49: typedef struct BlkdebugAIOCB { ! 50: BlockDriverAIOCB common; ! 51: QEMUBH *bh; ! 52: int ret; ! 53: } BlkdebugAIOCB; ! 54: ! 55: static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb); ! 56: ! 57: static AIOPool blkdebug_aio_pool = { ! 58: .aiocb_size = sizeof(BlkdebugAIOCB), ! 59: .cancel = blkdebug_aio_cancel, ! 60: }; ! 61: ! 62: enum { ! 63: ACTION_INJECT_ERROR, ! 64: ACTION_SET_STATE, ! 65: }; ! 66: ! 67: typedef struct BlkdebugRule { ! 68: BlkDebugEvent event; ! 69: int action; ! 70: int state; ! 71: union { ! 72: struct { ! 73: int error; ! 74: int immediately; ! 75: int once; ! 76: } inject; ! 77: struct { ! 78: int new_state; ! 79: } set_state; ! 80: } options; ! 81: QLIST_ENTRY(BlkdebugRule) next; ! 82: } BlkdebugRule; ! 83: ! 84: static QemuOptsList inject_error_opts = { ! 85: .name = "inject-error", ! 86: .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), ! 87: .desc = { ! 88: { ! 89: .name = "event", ! 90: .type = QEMU_OPT_STRING, ! 91: }, ! 92: { ! 93: .name = "state", ! 94: .type = QEMU_OPT_NUMBER, ! 95: }, ! 96: { ! 97: .name = "errno", ! 98: .type = QEMU_OPT_NUMBER, ! 99: }, ! 100: { ! 101: .name = "once", ! 102: .type = QEMU_OPT_BOOL, ! 103: }, ! 104: { ! 105: .name = "immediately", ! 106: .type = QEMU_OPT_BOOL, ! 107: }, ! 108: { /* end of list */ } ! 109: }, ! 110: }; ! 111: ! 112: static QemuOptsList set_state_opts = { ! 113: .name = "set-state", ! 114: .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), ! 115: .desc = { ! 116: { ! 117: .name = "event", ! 118: .type = QEMU_OPT_STRING, ! 119: }, ! 120: { ! 121: .name = "state", ! 122: .type = QEMU_OPT_NUMBER, ! 123: }, ! 124: { ! 125: .name = "new_state", ! 126: .type = QEMU_OPT_NUMBER, ! 127: }, ! 128: { /* end of list */ } ! 129: }, ! 130: }; ! 131: ! 132: static QemuOptsList *config_groups[] = { ! 133: &inject_error_opts, ! 134: &set_state_opts, ! 135: NULL ! 136: }; ! 137: ! 138: static const char *event_names[BLKDBG_EVENT_MAX] = { ! 139: [BLKDBG_L1_UPDATE] = "l1_update", ! 140: [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table", ! 141: [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table", ! 142: [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table", ! 143: ! 144: [BLKDBG_L2_LOAD] = "l2_load", ! 145: [BLKDBG_L2_UPDATE] = "l2_update", ! 146: [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", ! 147: [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", ! 148: [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", ! 149: ! 150: [BLKDBG_READ] = "read", ! 151: [BLKDBG_READ_AIO] = "read_aio", ! 152: [BLKDBG_READ_BACKING] = "read_backing", ! 153: [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", ! 154: [BLKDBG_READ_COMPRESSED] = "read_compressed", ! 155: ! 156: [BLKDBG_WRITE_AIO] = "write_aio", ! 157: [BLKDBG_WRITE_COMPRESSED] = "write_compressed", ! 158: ! 159: [BLKDBG_VMSTATE_LOAD] = "vmstate_load", ! 160: [BLKDBG_VMSTATE_SAVE] = "vmstate_save", ! 161: ! 162: [BLKDBG_COW_READ] = "cow_read", ! 163: [BLKDBG_COW_WRITE] = "cow_write", ! 164: ! 165: [BLKDBG_REFTABLE_LOAD] = "reftable_load", ! 166: [BLKDBG_REFTABLE_GROW] = "reftable_grow", ! 167: ! 168: [BLKDBG_REFBLOCK_LOAD] = "refblock_load", ! 169: [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", ! 170: [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", ! 171: [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", ! 172: [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup", ! 173: [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write", ! 174: [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks", ! 175: [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table", ! 176: [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table", ! 177: ! 178: [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", ! 179: [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", ! 180: [BLKDBG_CLUSTER_FREE] = "cluster_free", ! 181: }; ! 182: ! 183: static int get_event_by_name(const char *name, BlkDebugEvent *event) ! 184: { ! 185: int i; ! 186: ! 187: for (i = 0; i < BLKDBG_EVENT_MAX; i++) { ! 188: if (!strcmp(event_names[i], name)) { ! 189: *event = i; ! 190: return 0; ! 191: } ! 192: } ! 193: ! 194: return -1; ! 195: } ! 196: ! 197: struct add_rule_data { ! 198: BDRVBlkdebugState *s; ! 199: int action; ! 200: }; ! 201: ! 202: static int add_rule(QemuOpts *opts, void *opaque) ! 203: { ! 204: struct add_rule_data *d = opaque; ! 205: BDRVBlkdebugState *s = d->s; ! 206: const char* event_name; ! 207: BlkDebugEvent event; ! 208: struct BlkdebugRule *rule; ! 209: ! 210: /* Find the right event for the rule */ ! 211: event_name = qemu_opt_get(opts, "event"); ! 212: if (!event_name || get_event_by_name(event_name, &event) < 0) { ! 213: return -1; ! 214: } ! 215: ! 216: /* Set attributes common for all actions */ ! 217: rule = qemu_mallocz(sizeof(*rule)); ! 218: *rule = (struct BlkdebugRule) { ! 219: .event = event, ! 220: .action = d->action, ! 221: .state = qemu_opt_get_number(opts, "state", 0), ! 222: }; ! 223: ! 224: /* Parse action-specific options */ ! 225: switch (d->action) { ! 226: case ACTION_INJECT_ERROR: ! 227: rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); ! 228: rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); ! 229: rule->options.inject.immediately = ! 230: qemu_opt_get_bool(opts, "immediately", 0); ! 231: break; ! 232: ! 233: case ACTION_SET_STATE: ! 234: rule->options.set_state.new_state = ! 235: qemu_opt_get_number(opts, "new_state", 0); ! 236: break; ! 237: }; ! 238: ! 239: /* Add the rule */ ! 240: QLIST_INSERT_HEAD(&s->rules[event], rule, next); ! 241: ! 242: return 0; ! 243: } ! 244: ! 245: static int read_config(BDRVBlkdebugState *s, const char *filename) ! 246: { ! 247: FILE *f; ! 248: int ret; ! 249: struct add_rule_data d; ! 250: ! 251: f = fopen(filename, "r"); ! 252: if (f == NULL) { ! 253: return -errno; ! 254: } ! 255: ! 256: ret = qemu_config_parse(f, config_groups, filename); ! 257: if (ret < 0) { ! 258: goto fail; ! 259: } ! 260: ! 261: d.s = s; ! 262: d.action = ACTION_INJECT_ERROR; ! 263: qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0); ! 264: ! 265: d.action = ACTION_SET_STATE; ! 266: qemu_opts_foreach(&set_state_opts, add_rule, &d, 0); ! 267: ! 268: ret = 0; ! 269: fail: ! 270: qemu_opts_reset(&inject_error_opts); ! 271: qemu_opts_reset(&set_state_opts); ! 272: fclose(f); ! 273: return ret; ! 274: } ! 275: ! 276: /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ ! 277: static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags) ! 278: { ! 279: BDRVBlkdebugState *s = bs->opaque; ! 280: int ret; ! 281: char *config, *c; ! 282: ! 283: /* Parse the blkdebug: prefix */ ! 284: if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) { ! 285: return -EINVAL; ! 286: } ! 287: filename += strlen("blkdebug:"); ! 288: ! 289: /* Read rules from config file */ ! 290: c = strchr(filename, ':'); ! 291: if (c == NULL) { ! 292: return -EINVAL; ! 293: } ! 294: ! 295: config = strdup(filename); ! 296: config[c - filename] = '\0'; ! 297: ret = read_config(s, config); ! 298: free(config); ! 299: if (ret < 0) { ! 300: return ret; ! 301: } ! 302: filename = c + 1; ! 303: ! 304: /* Set initial state */ ! 305: s->vars.state = 1; ! 306: ! 307: /* Open the backing file */ ! 308: ret = bdrv_file_open(&bs->file, filename, flags); ! 309: if (ret < 0) { ! 310: return ret; ! 311: } ! 312: ! 313: return 0; ! 314: } ! 315: ! 316: static void error_callback_bh(void *opaque) ! 317: { ! 318: struct BlkdebugAIOCB *acb = opaque; ! 319: qemu_bh_delete(acb->bh); ! 320: acb->common.cb(acb->common.opaque, acb->ret); ! 321: qemu_aio_release(acb); ! 322: } ! 323: ! 324: static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb) ! 325: { ! 326: BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common); ! 327: qemu_aio_release(acb); ! 328: } ! 329: ! 330: static BlockDriverAIOCB *inject_error(BlockDriverState *bs, ! 331: BlockDriverCompletionFunc *cb, void *opaque) ! 332: { ! 333: BDRVBlkdebugState *s = bs->opaque; ! 334: int error = s->vars.inject_errno; ! 335: struct BlkdebugAIOCB *acb; ! 336: QEMUBH *bh; ! 337: ! 338: if (s->vars.inject_once) { ! 339: s->vars.inject_errno = 0; ! 340: } ! 341: ! 342: if (s->vars.inject_immediately) { ! 343: return NULL; ! 344: } ! 345: ! 346: acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque); ! 347: acb->ret = -error; ! 348: ! 349: bh = qemu_bh_new(error_callback_bh, acb); ! 350: acb->bh = bh; ! 351: qemu_bh_schedule(bh); ! 352: ! 353: return &acb->common; ! 354: } ! 355: ! 356: static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs, ! 357: int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, ! 358: BlockDriverCompletionFunc *cb, void *opaque) ! 359: { ! 360: BDRVBlkdebugState *s = bs->opaque; ! 361: ! 362: if (s->vars.inject_errno) { ! 363: return inject_error(bs, cb, opaque); ! 364: } ! 365: ! 366: BlockDriverAIOCB *acb = ! 367: bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); ! 368: return acb; ! 369: } ! 370: ! 371: static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, ! 372: int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, ! 373: BlockDriverCompletionFunc *cb, void *opaque) ! 374: { ! 375: BDRVBlkdebugState *s = bs->opaque; ! 376: ! 377: if (s->vars.inject_errno) { ! 378: return inject_error(bs, cb, opaque); ! 379: } ! 380: ! 381: BlockDriverAIOCB *acb = ! 382: bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); ! 383: return acb; ! 384: } ! 385: ! 386: static void blkdebug_close(BlockDriverState *bs) ! 387: { ! 388: BDRVBlkdebugState *s = bs->opaque; ! 389: BlkdebugRule *rule, *next; ! 390: int i; ! 391: ! 392: for (i = 0; i < BLKDBG_EVENT_MAX; i++) { ! 393: QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { ! 394: QLIST_REMOVE(rule, next); ! 395: qemu_free(rule); ! 396: } ! 397: } ! 398: } ! 399: ! 400: static void blkdebug_flush(BlockDriverState *bs) ! 401: { ! 402: bdrv_flush(bs->file); ! 403: } ! 404: ! 405: static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs, ! 406: BlockDriverCompletionFunc *cb, void *opaque) ! 407: { ! 408: return bdrv_aio_flush(bs->file, cb, opaque); ! 409: } ! 410: ! 411: static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, ! 412: BlkdebugVars *old_vars) ! 413: { ! 414: BDRVBlkdebugState *s = bs->opaque; ! 415: BlkdebugVars *vars = &s->vars; ! 416: ! 417: /* Only process rules for the current state */ ! 418: if (rule->state && rule->state != old_vars->state) { ! 419: return; ! 420: } ! 421: ! 422: /* Take the action */ ! 423: switch (rule->action) { ! 424: case ACTION_INJECT_ERROR: ! 425: vars->inject_errno = rule->options.inject.error; ! 426: vars->inject_once = rule->options.inject.once; ! 427: vars->inject_immediately = rule->options.inject.immediately; ! 428: break; ! 429: ! 430: case ACTION_SET_STATE: ! 431: vars->state = rule->options.set_state.new_state; ! 432: break; ! 433: } ! 434: } ! 435: ! 436: static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) ! 437: { ! 438: BDRVBlkdebugState *s = bs->opaque; ! 439: struct BlkdebugRule *rule; ! 440: BlkdebugVars old_vars = s->vars; ! 441: ! 442: if (event < 0 || event >= BLKDBG_EVENT_MAX) { ! 443: return; ! 444: } ! 445: ! 446: QLIST_FOREACH(rule, &s->rules[event], next) { ! 447: process_rule(bs, rule, &old_vars); ! 448: } ! 449: } ! 450: ! 451: static BlockDriver bdrv_blkdebug = { ! 452: .format_name = "blkdebug", ! 453: .protocol_name = "blkdebug", ! 454: ! 455: .instance_size = sizeof(BDRVBlkdebugState), ! 456: ! 457: .bdrv_file_open = blkdebug_open, ! 458: .bdrv_close = blkdebug_close, ! 459: .bdrv_flush = blkdebug_flush, ! 460: ! 461: .bdrv_aio_readv = blkdebug_aio_readv, ! 462: .bdrv_aio_writev = blkdebug_aio_writev, ! 463: .bdrv_aio_flush = blkdebug_aio_flush, ! 464: ! 465: .bdrv_debug_event = blkdebug_debug_event, ! 466: }; ! 467: ! 468: static void bdrv_blkdebug_init(void) ! 469: { ! 470: bdrv_register(&bdrv_blkdebug); ! 471: } ! 472: ! 473: block_init(bdrv_blkdebug_init);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.