int state;
union {
struct {
+ uint64_t iotype_mask;
int error;
int immediately;
int once;
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule;
+QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
+ "BlkdebugIOType mask does not fit into an uint64_t");
+
static QemuOptsList inject_error_opts = {
.name = "inject-error",
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
.name = "state",
.type = QEMU_OPT_NUMBER,
},
+ {
+ .name = "iotype",
+ .type = QEMU_OPT_STRING,
+ },
{
.name = "errno",
.type = QEMU_OPT_NUMBER,
int event;
struct BlkdebugRule *rule;
int64_t sector;
+ BlkdebugIOType iotype;
+ Error *local_error = NULL;
/* Find the right event for the rule */
event_name = qemu_opt_get(opts, "event");
sector = qemu_opt_get_number(opts, "sector", -1);
rule->options.inject.offset =
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
+
+ iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
+ qemu_opt_get(opts, "iotype"),
+ BLKDEBUG_IO_TYPE__MAX, &local_error);
+ if (local_error) {
+ error_propagate(errp, local_error);
+ return -1;
+ }
+ if (iotype != BLKDEBUG_IO_TYPE__MAX) {
+ rule->options.inject.iotype_mask = (1ull << iotype);
+ } else {
+ /* Apply the default */
+ rule->options.inject.iotype_mask =
+ (1ull << BLKDEBUG_IO_TYPE_READ)
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE)
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
+ | (1ull << BLKDEBUG_IO_TYPE_DISCARD)
+ | (1ull << BLKDEBUG_IO_TYPE_FLUSH);
+ }
+
break;
case ACTION_SET_STATE:
return ret;
}
-static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
+static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ BlkdebugIOType iotype)
{
BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
uint64_t inject_offset = rule->options.inject.offset;
- if (inject_offset == -1 ||
- (bytes && inject_offset >= offset &&
- inject_offset < offset + bytes))
+ if ((inject_offset == -1 ||
+ (bytes && inject_offset >= offset &&
+ inject_offset < offset + bytes)) &&
+ (rule->options.inject.iotype_mask & (1ull << iotype)))
{
break;
}
assert(bytes <= bs->bl.max_transfer);
}
- err = rule_check(bs, offset, bytes);
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
if (err) {
return err;
}
assert(bytes <= bs->bl.max_transfer);
}
- err = rule_check(bs, offset, bytes);
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
if (err) {
return err;
}
static int blkdebug_co_flush(BlockDriverState *bs)
{
- int err = rule_check(bs, 0, 0);
+ int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
if (err) {
return err;
assert(bytes <= bs->bl.max_pwrite_zeroes);
}
- err = rule_check(bs, offset, bytes);
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
if (err) {
return err;
}
assert(bytes <= bs->bl.max_pdiscard);
}
- err = rule_check(bs, offset, bytes);
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
if (err) {
return err;
}
'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
'cor_write', 'cluster_alloc_space'] }
+##
+# @BlkdebugIOType:
+#
+# Kinds of I/O that blkdebug can inject errors in.
+#
+# @read: .bdrv_co_preadv()
+#
+# @write: .bdrv_co_pwritev()
+#
+# @write-zeroes: .bdrv_co_pwrite_zeroes()
+#
+# @discard: .bdrv_co_pdiscard()
+#
+# @flush: .bdrv_co_flush_to_disk()
+#
+# Since: 4.1
+##
+{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush' ] }
+
##
# @BlkdebugInjectErrorOptions:
#
# @state: the state identifier blkdebug needs to be in to
# actually trigger the event; defaults to "any"
#
+# @iotype: the type of I/O operations on which this error should
+# be injected; defaults to "all read, write,
+# write-zeroes, discard, and flush operations"
+# (since: 4.1)
+#
# @errno: error identifier (errno) to be returned; defaults to
# EIO
#
{ 'struct': 'BlkdebugInjectErrorOptions',
'data': { 'event': 'BlkdebugEvent',
'*state': 'int',
+ '*iotype': 'BlkdebugIOType',
'*errno': 'int',
'*sector': 'int',
'*once': 'bool',