#include "qemu/config-file.h"
#include "block/block_int.h"
#include "qemu/module.h"
-#include "qapi/qmp/qbool.h"
+#include "qemu/option.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "sysemu/qtest.h"
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,
NULL
};
-static int get_event_by_name(const char *name, BlkdebugEvent *event)
-{
- int i;
-
- for (i = 0; i < BLKDBG__MAX; i++) {
- if (!strcmp(BlkdebugEvent_lookup[i], name)) {
- *event = i;
- return 0;
- }
- }
-
- return -1;
-}
-
struct add_rule_data {
BDRVBlkdebugState *s;
int action;
struct add_rule_data *d = opaque;
BDRVBlkdebugState *s = d->s;
const char* event_name;
- BlkdebugEvent event;
+ 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");
if (!event_name) {
error_setg(errp, "Missing event name for rule");
return -1;
- } else if (get_event_by_name(event_name, &event) < 0) {
- error_setg(errp, "Invalid event name \"%s\"", event_name);
+ }
+ event = qapi_enum_parse(&BlkdebugEvent_lookup, event_name, -1, errp);
+ if (event < 0) {
return -1;
}
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:
ret = qemu_config_parse(f, config_groups, filename);
if (ret < 0) {
error_setg(errp, "Could not parse blkdebug config file");
- ret = -EINVAL;
goto fail;
}
}
if (c != filename) {
QString *config_path;
- config_path = qstring_from_substr(filename, 0, c - filename - 1);
+ config_path = qstring_from_substr(filename, 0, c - filename);
qdict_put(options, "config", config_path);
}
goto out;
}
- bs->supported_write_flags = BDRV_REQ_FUA &
- bs->file->bs->supported_write_flags;
- bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
- bs->file->bs->supported_zero_flags;
+ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
+ (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
+ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
+ ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
+ bs->file->bs->supported_zero_flags);
ret = -EINVAL;
/* Set alignment overrides */
goto out;
}
+ bdrv_debug_event(bs, BLKDBG_NONE);
+
ret = 0;
out:
if (ret < 0) {
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;
}
- return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
+ return bdrv_co_pdiscard(bs->file, offset, bytes);
+}
+
+static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset,
+ int64_t bytes,
+ int64_t *pnum,
+ int64_t *map,
+ BlockDriverState **file)
+{
+ int err;
+
+ assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
+
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
+ if (err) {
+ return err;
+ }
+
+ return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
+ pnum, map, file);
}
static void blkdebug_close(BlockDriverState *bs)
{
BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule;
- BlkdebugEvent blkdebug_event;
+ int blkdebug_event;
- if (get_event_by_name(event, &blkdebug_event) < 0) {
+ blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL);
+ if (blkdebug_event < 0) {
return -ENOENT;
}
-
rule = g_malloc(sizeof(*rule));
*rule = (struct BlkdebugRule) {
.event = blkdebug_event,
return bdrv_getlength(bs->file->bs);
}
-static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
-{
- return bdrv_truncate(bs->file, offset, errp);
-}
-
-static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
+static void blkdebug_refresh_filename(BlockDriverState *bs)
{
BDRVBlkdebugState *s = bs->opaque;
- QDict *opts;
const QDictEntry *e;
- bool force_json = false;
-
- for (e = qdict_first(options); e; e = qdict_next(options, e)) {
- if (strcmp(qdict_entry_key(e), "config") &&
- strcmp(qdict_entry_key(e), "x-image"))
- {
- force_json = true;
- break;
- }
- }
+ int ret;
- if (force_json && !bs->file->bs->full_open_options) {
- /* The config file cannot be recreated, so creating a plain filename
- * is impossible */
+ if (!bs->file->bs->exact_filename[0]) {
return;
}
- if (!force_json && bs->file->bs->exact_filename[0]) {
- int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
- "blkdebug:%s:%s", s->config_file ?: "",
- bs->file->bs->exact_filename);
- if (ret >= sizeof(bs->exact_filename)) {
- /* An overflow makes the filename unusable, so do not report any */
- bs->exact_filename[0] = 0;
+ for (e = qdict_first(bs->full_open_options); e;
+ e = qdict_next(bs->full_open_options, e))
+ {
+ /* Real child options are under "image", but "x-image" may
+ * contain a filename */
+ if (strcmp(qdict_entry_key(e), "config") &&
+ strcmp(qdict_entry_key(e), "image") &&
+ strcmp(qdict_entry_key(e), "x-image") &&
+ strcmp(qdict_entry_key(e), "driver"))
+ {
+ return;
}
}
- opts = qdict_new();
- qdict_put_str(opts, "driver", "blkdebug");
-
- QINCREF(bs->file->bs->full_open_options);
- qdict_put(opts, "image", bs->file->bs->full_open_options);
-
- for (e = qdict_first(options); e; e = qdict_next(options, e)) {
- if (strcmp(qdict_entry_key(e), "x-image")) {
- qobject_incref(qdict_entry_value(e));
- qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
- }
+ ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+ "blkdebug:%s:%s",
+ s->config_file ?: "", bs->file->bs->exact_filename);
+ if (ret >= sizeof(bs->exact_filename)) {
+ /* An overflow makes the filename unusable, so do not report any */
+ bs->exact_filename[0] = 0;
}
-
- bs->full_open_options = opts;
}
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
return 0;
}
+static const char *const blkdebug_strong_runtime_opts[] = {
+ "config",
+ "inject-error.",
+ "set-state.",
+ "align",
+ "max-transfer",
+ "opt-write-zero",
+ "max-write-zero",
+ "opt-discard",
+ "max-discard",
+
+ NULL
+};
+
static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug",
.protocol_name = "blkdebug",
.instance_size = sizeof(BDRVBlkdebugState),
+ .is_filter = true,
.bdrv_parse_filename = blkdebug_parse_filename,
.bdrv_file_open = blkdebug_open,
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkdebug_getlength,
- .bdrv_truncate = blkdebug_truncate,
.bdrv_refresh_filename = blkdebug_refresh_filename,
.bdrv_refresh_limits = blkdebug_refresh_limits,
.bdrv_co_flush_to_disk = blkdebug_co_flush,
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
+ .bdrv_co_block_status = blkdebug_co_block_status,
.bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
= blkdebug_debug_remove_breakpoint,
.bdrv_debug_resume = blkdebug_debug_resume,
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
+
+ .strong_runtime_opts = blkdebug_strong_runtime_opts,
};
static void bdrv_blkdebug_init(void)