Block patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJUJbcwAAoJEH8JsnLIjy/WmckP/RBMK3gkr2aArp0k/sbdFcnW D7GusGkoj1jXRVFKuG3d+T/nozJTX0mkfnaRo8QCg/5VVJtqeHfsfY6JHgppQEai C5Z8gtzIFxsQNE0/8d2D6zJMnqsIeXAVtDONWgWcDOZstVXn+KEKOw5E3KCZeGxF yLA+h4h8sncT3ysPE0/jnQqeIwGJpLusFXmICiRY2lXRcCgY/qniymUq25FZO69m Isw8iVHrUr05u1kfthdQeSAmdPQqI5tK5siR4CERtO2XdlyiODmdo5DRZmATfGiL CGUIJd30mvrKpqRpIk8gbuGEtz92RuTnyoi9xkLcKi52eWyflGH4gjN3ojYe5Pro hRY06AcwI9lAVjCy7lKPO6dJn8RWOi/wkgfffK0CFujlo8r1BtwfCW0Y9koYuhdq mM/K0IEbjNgOvacHLIMOTQuKz3DLv7QB8wZQ+cTkdmvC6kNGnqD9yrA4cAQP4kjR wjm/WfDIRKPif6cMwgaOiQeNaL0VF2LIn4GRWgaiIqEYfcKzy65uBFoPqOlAoXr8 NtTzCIXDIBbTSzz3VLBKD22FOsP0pfPrMeXkf+GwJwStKMcp6PGY4iUd3+M3bfOP iUsxTJ4Uw5zMg0MgOaaWnUaDdbRtKGtH1y2UA+4X3gO+3W8Vz0e8B5cPMBqN/mpy lUS4mBA08mTvaLE520rp =ManP -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block patches # gpg: Signature made Fri 26 Sep 2014 19:57:52 BST using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: qemu-iotests: Fail test if explicit test case number is unknown block: Validate node-name vpc: fix beX_to_cpu() and cpu_to_beX() confusion docs: add blkdebug block driver documentation block: Catch simultaneous usage of options and their aliases block: Specify -drive legacy option aliases in array block: Improve message for device name clashing with node name qemu-nbd: Destroy the BlockDriverState properly block: Keep DriveInfo alive until BlockDriverState dies blockdev: Disentangle BlockDriverState and DriveInfo creation blkdebug: show an error for invalid event names Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0ebcc56453
21
block.c
21
block.c
@ -29,6 +29,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/blockdev.h" /* FIXME layering violation */
|
||||
#include "qemu/notify.h"
|
||||
#include "block/coroutine.h"
|
||||
#include "block/qapi.h"
|
||||
@ -334,19 +335,30 @@ void bdrv_register(BlockDriver *bdrv)
|
||||
QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
|
||||
}
|
||||
|
||||
static bool bdrv_is_valid_name(const char *name)
|
||||
{
|
||||
return qemu_opts_id_wellformed(name);
|
||||
}
|
||||
|
||||
/* create a new block device (by default it is empty) */
|
||||
BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int i;
|
||||
|
||||
if (*device_name && !bdrv_is_valid_name(device_name)) {
|
||||
error_setg(errp, "Invalid device name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bdrv_find(device_name)) {
|
||||
error_setg(errp, "Device with id '%s' already exists",
|
||||
device_name);
|
||||
return NULL;
|
||||
}
|
||||
if (bdrv_find_node(device_name)) {
|
||||
error_setg(errp, "Device with node-name '%s' already exists",
|
||||
error_setg(errp,
|
||||
"Device name '%s' conflicts with an existing node name",
|
||||
device_name);
|
||||
return NULL;
|
||||
}
|
||||
@ -861,9 +873,9 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
|
||||
return;
|
||||
}
|
||||
|
||||
/* empty string node name is invalid */
|
||||
if (node_name[0] == '\0') {
|
||||
error_setg(errp, "Empty node name");
|
||||
/* Check for empty string or invalid characters */
|
||||
if (!bdrv_is_valid_name(node_name)) {
|
||||
error_setg(errp, "Invalid node name");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2110,6 +2122,7 @@ static void bdrv_delete(BlockDriverState *bs)
|
||||
/* remove from list, if necessary */
|
||||
bdrv_make_anon(bs);
|
||||
|
||||
drive_info_del(drive_get_by_blockdev(bs));
|
||||
g_free(bs);
|
||||
}
|
||||
|
||||
|
@ -214,6 +214,7 @@ static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||
struct add_rule_data {
|
||||
BDRVBlkdebugState *s;
|
||||
int action;
|
||||
Error **errp;
|
||||
};
|
||||
|
||||
static int add_rule(QemuOpts *opts, void *opaque)
|
||||
@ -226,7 +227,11 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
|
||||
/* Find the right event for the rule */
|
||||
event_name = qemu_opt_get(opts, "event");
|
||||
if (!event_name || get_event_by_name(event_name, &event) < 0) {
|
||||
if (!event_name) {
|
||||
error_setg(d->errp, "Missing event name for rule");
|
||||
return -1;
|
||||
} else if (get_event_by_name(event_name, &event) < 0) {
|
||||
error_setg(d->errp, "Invalid event name \"%s\"", event_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -312,10 +317,21 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
|
||||
|
||||
d.s = s;
|
||||
d.action = ACTION_INJECT_ERROR;
|
||||
qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0);
|
||||
d.errp = &local_err;
|
||||
qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
d.action = ACTION_SET_STATE;
|
||||
qemu_opts_foreach(&set_state_opts, add_rule, &d, 0);
|
||||
qemu_opts_foreach(&set_state_opts, add_rule, &d, 1);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
|
44
block/vpc.c
44
block/vpc.c
@ -207,7 +207,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
"incorrect.\n", bs->filename);
|
||||
|
||||
/* Write 'checksum' back to footer, or else will leave it with zero. */
|
||||
footer->checksum = be32_to_cpu(checksum);
|
||||
footer->checksum = cpu_to_be32(checksum);
|
||||
|
||||
// The visible size of a image in Virtual PC depends on the geometry
|
||||
// rather than on the size stored in the footer (the size in the footer
|
||||
@ -472,7 +472,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
|
||||
|
||||
// Write BAT entry to disk
|
||||
bat_offset = s->bat_offset + (4 * index);
|
||||
bat_value = be32_to_cpu(s->pagetable[index]);
|
||||
bat_value = cpu_to_be32(s->pagetable[index]);
|
||||
ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
@ -699,13 +699,13 @@ static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf,
|
||||
* Note: The spec is actually wrong here for data_offset, it says
|
||||
* 0xFFFFFFFF, but MS tools expect all 64 bits to be set.
|
||||
*/
|
||||
dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
||||
dyndisk_header->table_offset = be64_to_cpu(3 * 512);
|
||||
dyndisk_header->version = be32_to_cpu(0x00010000);
|
||||
dyndisk_header->block_size = be32_to_cpu(block_size);
|
||||
dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
|
||||
dyndisk_header->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
|
||||
dyndisk_header->table_offset = cpu_to_be64(3 * 512);
|
||||
dyndisk_header->version = cpu_to_be32(0x00010000);
|
||||
dyndisk_header->block_size = cpu_to_be32(block_size);
|
||||
dyndisk_header->max_table_entries = cpu_to_be32(num_bat_entries);
|
||||
|
||||
dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
|
||||
dyndisk_header->checksum = cpu_to_be32(vpc_checksum(buf, 1024));
|
||||
|
||||
// Write the header
|
||||
offset = 512;
|
||||
@ -810,36 +810,36 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
memcpy(footer->creator_app, "qemu", 4);
|
||||
memcpy(footer->creator_os, "Wi2k", 4);
|
||||
|
||||
footer->features = be32_to_cpu(0x02);
|
||||
footer->version = be32_to_cpu(0x00010000);
|
||||
footer->features = cpu_to_be32(0x02);
|
||||
footer->version = cpu_to_be32(0x00010000);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->data_offset = be64_to_cpu(HEADER_SIZE);
|
||||
footer->data_offset = cpu_to_be64(HEADER_SIZE);
|
||||
} else {
|
||||
footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
|
||||
footer->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
footer->timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE);
|
||||
|
||||
/* Version of Virtual PC 2007 */
|
||||
footer->major = be16_to_cpu(0x0005);
|
||||
footer->minor = be16_to_cpu(0x0003);
|
||||
footer->major = cpu_to_be16(0x0005);
|
||||
footer->minor = cpu_to_be16(0x0003);
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
footer->orig_size = be64_to_cpu(total_sectors * 512);
|
||||
footer->size = be64_to_cpu(total_sectors * 512);
|
||||
footer->orig_size = cpu_to_be64(total_sectors * 512);
|
||||
footer->size = cpu_to_be64(total_sectors * 512);
|
||||
} else {
|
||||
footer->orig_size = be64_to_cpu(total_size);
|
||||
footer->size = be64_to_cpu(total_size);
|
||||
footer->orig_size = cpu_to_be64(total_size);
|
||||
footer->size = cpu_to_be64(total_size);
|
||||
}
|
||||
footer->cyls = be16_to_cpu(cyls);
|
||||
footer->cyls = cpu_to_be16(cyls);
|
||||
footer->heads = heads;
|
||||
footer->secs_per_cyl = secs_per_cyl;
|
||||
|
||||
footer->type = be32_to_cpu(disk_type);
|
||||
footer->type = cpu_to_be32(disk_type);
|
||||
|
||||
#if defined(CONFIG_UUID)
|
||||
uuid_generate(footer->uuid);
|
||||
#endif
|
||||
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
if (disk_type == VHD_DYNAMIC) {
|
||||
ret = create_dynamic_disk(bs, buf, total_sectors);
|
||||
|
103
blockdev.c
103
blockdev.c
@ -216,11 +216,17 @@ static void bdrv_format_print(void *opaque, const char *name)
|
||||
|
||||
void drive_del(DriveInfo *dinfo)
|
||||
{
|
||||
bdrv_unref(dinfo->bdrv);
|
||||
}
|
||||
|
||||
void drive_info_del(DriveInfo *dinfo)
|
||||
{
|
||||
if (!dinfo) {
|
||||
return;
|
||||
}
|
||||
if (dinfo->opts) {
|
||||
qemu_opts_del(dinfo->opts);
|
||||
}
|
||||
|
||||
bdrv_unref(dinfo->bdrv);
|
||||
g_free(dinfo->id);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
g_free(dinfo->serial);
|
||||
@ -301,6 +307,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||
int ro = 0;
|
||||
int bdrv_flags = 0;
|
||||
int on_read_error, on_write_error;
|
||||
BlockDriverState *bs;
|
||||
DriveInfo *dinfo;
|
||||
ThrottleConfig cfg;
|
||||
int snapshot = 0;
|
||||
@ -456,26 +463,27 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
|
||||
/* init */
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
dinfo->id = g_strdup(qemu_opts_id(opts));
|
||||
dinfo->bdrv = bdrv_new(dinfo->id, &error);
|
||||
if (error) {
|
||||
error_propagate(errp, error);
|
||||
goto bdrv_new_err;
|
||||
bs = bdrv_new(qemu_opts_id(opts), errp);
|
||||
if (!bs) {
|
||||
goto early_err;
|
||||
}
|
||||
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
dinfo->bdrv->read_only = ro;
|
||||
dinfo->bdrv->detect_zeroes = detect_zeroes;
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
||||
bs->read_only = ro;
|
||||
bs->detect_zeroes = detect_zeroes;
|
||||
|
||||
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
||||
bdrv_set_on_error(bs, on_read_error, on_write_error);
|
||||
|
||||
/* disk I/O throttling */
|
||||
if (throttle_enabled(&cfg)) {
|
||||
bdrv_io_limits_enable(dinfo->bdrv);
|
||||
bdrv_set_io_limits(dinfo->bdrv, &cfg);
|
||||
bdrv_io_limits_enable(bs);
|
||||
bdrv_set_io_limits(bs, &cfg);
|
||||
}
|
||||
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
dinfo->id = g_strdup(qemu_opts_id(opts));
|
||||
dinfo->bdrv = bs;
|
||||
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
||||
|
||||
if (!file || !*file) {
|
||||
if (has_driver_specific_opts) {
|
||||
file = NULL;
|
||||
@ -502,7 +510,8 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
||||
|
||||
QINCREF(bs_opts);
|
||||
ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error);
|
||||
ret = bdrv_open(&bs, file, NULL, bs_opts, bdrv_flags, drv, &error);
|
||||
assert(bs == dinfo->bdrv);
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "could not open disk image %s: %s",
|
||||
@ -511,8 +520,9 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bdrv_key_required(dinfo->bdrv))
|
||||
if (bdrv_key_required(bs)) {
|
||||
autostart = 0;
|
||||
}
|
||||
|
||||
QDECREF(bs_opts);
|
||||
qemu_opts_del(opts);
|
||||
@ -520,11 +530,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
||||
return dinfo;
|
||||
|
||||
err:
|
||||
bdrv_unref(dinfo->bdrv);
|
||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||
bdrv_new_err:
|
||||
g_free(dinfo->id);
|
||||
g_free(dinfo);
|
||||
bdrv_unref(bs);
|
||||
early_err:
|
||||
qemu_opts_del(opts);
|
||||
err_no_opts:
|
||||
@ -532,12 +538,18 @@ err_no_opts:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to)
|
||||
static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
|
||||
Error **errp)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
value = qemu_opt_get(opts, from);
|
||||
if (value) {
|
||||
if (qemu_opt_find(opts, to)) {
|
||||
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
|
||||
"same time", to, from);
|
||||
return;
|
||||
}
|
||||
qemu_opt_set(opts, to, value);
|
||||
qemu_opt_unset(opts, from);
|
||||
}
|
||||
@ -641,28 +653,43 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
const char *serial;
|
||||
const char *filename;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
|
||||
qemu_opt_rename(all_opts, "iops_rd", "throttling.iops-read");
|
||||
qemu_opt_rename(all_opts, "iops_wr", "throttling.iops-write");
|
||||
static const struct {
|
||||
const char *from;
|
||||
const char *to;
|
||||
} opt_renames[] = {
|
||||
{ "iops", "throttling.iops-total" },
|
||||
{ "iops_rd", "throttling.iops-read" },
|
||||
{ "iops_wr", "throttling.iops-write" },
|
||||
|
||||
qemu_opt_rename(all_opts, "bps", "throttling.bps-total");
|
||||
qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read");
|
||||
qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write");
|
||||
{ "bps", "throttling.bps-total" },
|
||||
{ "bps_rd", "throttling.bps-read" },
|
||||
{ "bps_wr", "throttling.bps-write" },
|
||||
|
||||
qemu_opt_rename(all_opts, "iops_max", "throttling.iops-total-max");
|
||||
qemu_opt_rename(all_opts, "iops_rd_max", "throttling.iops-read-max");
|
||||
qemu_opt_rename(all_opts, "iops_wr_max", "throttling.iops-write-max");
|
||||
{ "iops_max", "throttling.iops-total-max" },
|
||||
{ "iops_rd_max", "throttling.iops-read-max" },
|
||||
{ "iops_wr_max", "throttling.iops-write-max" },
|
||||
|
||||
qemu_opt_rename(all_opts, "bps_max", "throttling.bps-total-max");
|
||||
qemu_opt_rename(all_opts, "bps_rd_max", "throttling.bps-read-max");
|
||||
qemu_opt_rename(all_opts, "bps_wr_max", "throttling.bps-write-max");
|
||||
{ "bps_max", "throttling.bps-total-max" },
|
||||
{ "bps_rd_max", "throttling.bps-read-max" },
|
||||
{ "bps_wr_max", "throttling.bps-write-max" },
|
||||
|
||||
qemu_opt_rename(all_opts,
|
||||
"iops_size", "throttling.iops-size");
|
||||
{ "iops_size", "throttling.iops-size" },
|
||||
|
||||
qemu_opt_rename(all_opts, "readonly", "read-only");
|
||||
{ "readonly", "read-only" },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(opt_renames); i++) {
|
||||
qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_report("%s", error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
value = qemu_opt_get(all_opts, "cache");
|
||||
if (value) {
|
||||
|
161
docs/blkdebug.txt
Normal file
161
docs/blkdebug.txt
Normal file
@ -0,0 +1,161 @@
|
||||
Block I/O error injection using blkdebug
|
||||
----------------------------------------
|
||||
Copyright (C) 2014 Red Hat Inc
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
the COPYING file in the top-level directory.
|
||||
|
||||
The blkdebug block driver is a rule-based error injection engine. It can be
|
||||
used to exercise error code paths in block drivers including ENOSPC (out of
|
||||
space) and EIO.
|
||||
|
||||
This document gives an overview of the features available in blkdebug.
|
||||
|
||||
Background
|
||||
----------
|
||||
Block drivers have many error code paths that handle I/O errors. Image formats
|
||||
are especially complex since metadata I/O errors during cluster allocation or
|
||||
while updating tables happen halfway through request processing and require
|
||||
discipline to keep image files consistent.
|
||||
|
||||
Error injection allows test cases to trigger I/O errors at specific points.
|
||||
This way, all error paths can be tested to make sure they are correct.
|
||||
|
||||
Rules
|
||||
-----
|
||||
The blkdebug block driver takes a list of "rules" that tell the error injection
|
||||
engine when to fail an I/O request.
|
||||
|
||||
Each I/O request is evaluated against the rules. If a rule matches the request
|
||||
then its "action" is executed.
|
||||
|
||||
Rules can be placed in a configuration file; the configuration file
|
||||
follows the same .ini-like format used by QEMU's -readconfig option, and
|
||||
each section of the file represents a rule.
|
||||
|
||||
The following configuration file defines a single rule:
|
||||
|
||||
$ cat blkdebug.conf
|
||||
[inject-error]
|
||||
event = "read_aio"
|
||||
errno = "28"
|
||||
|
||||
This rule fails all aio read requests with ENOSPC (28). Note that the errno
|
||||
value depends on the host. On Linux, see
|
||||
/usr/include/asm-generic/errno-base.h for errno values.
|
||||
|
||||
Invoke QEMU as follows:
|
||||
|
||||
$ qemu-system-x86_64
|
||||
-drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \
|
||||
-device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0
|
||||
|
||||
Rules support the following attributes:
|
||||
|
||||
event - which type of operation to match (e.g. read_aio, write_aio,
|
||||
flush_to_os, flush_to_disk). See the "Events" section for
|
||||
information on events.
|
||||
|
||||
state - (optional) the engine must be in this state number in order for this
|
||||
rule to match. See the "State transitions" section for information
|
||||
on states.
|
||||
|
||||
errno - the numeric errno value to return when a request matches this rule.
|
||||
The errno values depend on the host since the numeric values are not
|
||||
standarized in the POSIX specification.
|
||||
|
||||
sector - (optional) a sector number that the request must overlap in order to
|
||||
match this rule
|
||||
|
||||
once - (optional, default "off") only execute this action on the first
|
||||
matching request
|
||||
|
||||
immediately - (optional, default "off") return a NULL BlockDriverAIOCB
|
||||
pointer and fail without an errno instead. This exercises the
|
||||
code path where BlockDriverAIOCB fails and the caller's
|
||||
BlockDriverCompletionFunc is not invoked.
|
||||
|
||||
Events
|
||||
------
|
||||
Block drivers provide information about the type of I/O request they are about
|
||||
to make so rules can match specific types of requests. For example, the qcow2
|
||||
block driver tells blkdebug when it accesses the L1 table so rules can match
|
||||
only L1 table accesses and not other metadata or guest data requests.
|
||||
|
||||
The core events are:
|
||||
|
||||
read_aio - guest data read
|
||||
|
||||
write_aio - guest data write
|
||||
|
||||
flush_to_os - write out unwritten block driver state (e.g. cached metadata)
|
||||
|
||||
flush_to_disk - flush the host block device's disk cache
|
||||
|
||||
See block/blkdebug.c:event_names[] for the full list of events. You may need
|
||||
to grep block driver source code to understand the meaning of specific events.
|
||||
|
||||
State transitions
|
||||
-----------------
|
||||
There are cases where more power is needed to match a particular I/O request in
|
||||
a longer sequence of requests. For example:
|
||||
|
||||
write_aio
|
||||
flush_to_disk
|
||||
write_aio
|
||||
|
||||
How do we match the 2nd write_aio but not the first? This is where state
|
||||
transitions come in.
|
||||
|
||||
The error injection engine has an integer called the "state" that always starts
|
||||
initialized to 1. The state integer is internal to blkdebug and cannot be
|
||||
observed from outside but rules can interact with it for powerful matching
|
||||
behavior.
|
||||
|
||||
Rules can be conditional on the current state and they can transition to a new
|
||||
state.
|
||||
|
||||
When a rule's "state" attribute is non-zero then the current state must equal
|
||||
the attribute in order for the rule to match.
|
||||
|
||||
For example, to match the 2nd write_aio:
|
||||
|
||||
[set-state]
|
||||
event = "write_aio"
|
||||
state = "1"
|
||||
new_state = "2"
|
||||
|
||||
[inject-error]
|
||||
event = "write_aio"
|
||||
state = "2"
|
||||
errno = "5"
|
||||
|
||||
The first write_aio request matches the set-state rule and transitions from
|
||||
state 1 to state 2. Once state 2 has been entered, the set-state rule no
|
||||
longer matches since it requires state 1. But the inject-error rule now
|
||||
matches the next write_aio request and injects EIO (5).
|
||||
|
||||
State transition rules support the following attributes:
|
||||
|
||||
event - which type of operation to match (e.g. read_aio, write_aio,
|
||||
flush_to_os, flush_to_disk). See the "Events" section for
|
||||
information on events.
|
||||
|
||||
state - (optional) the engine must be in this state number in order for this
|
||||
rule to match
|
||||
|
||||
new_state - transition to this state number
|
||||
|
||||
Suspend and resume
|
||||
------------------
|
||||
Exercising code paths in block drivers may require specific ordering amongst
|
||||
concurrent requests. The "breakpoint" feature allows requests to be halted on
|
||||
a blkdebug event and resumed later. This makes it possible to achieve
|
||||
deterministic ordering when multiple requests are in flight.
|
||||
|
||||
Breakpoints on blkdebug events are associated with a user-defined "tag" string.
|
||||
This tag serves as an identifier by which the request can be resumed at a later
|
||||
point.
|
||||
|
||||
See the qemu-io(1) break, resume, remove_break, and wait_break commands for
|
||||
details.
|
@ -103,6 +103,7 @@ typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaq
|
||||
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
|
||||
int abort_on_failure);
|
||||
|
||||
int qemu_opts_id_wellformed(const char *id);
|
||||
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
|
||||
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
||||
int fail_if_exists, Error **errp);
|
||||
|
@ -56,6 +56,7 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
||||
const char *optstr);
|
||||
DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type);
|
||||
void drive_del(DriveInfo *dinfo);
|
||||
void drive_info_del(DriveInfo *dinfo);
|
||||
|
||||
/* device-hotplug */
|
||||
|
||||
|
@ -1011,14 +1011,14 @@ static int img_compare(int argc, char **argv)
|
||||
goto out3;
|
||||
}
|
||||
|
||||
bs1 = bdrv_new_open("image 1", filename1, fmt1, flags, true, quiet);
|
||||
bs1 = bdrv_new_open("image_1", filename1, fmt1, flags, true, quiet);
|
||||
if (!bs1) {
|
||||
error_report("Can't open file %s", filename1);
|
||||
ret = 2;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
bs2 = bdrv_new_open("image 2", filename2, fmt2, flags, true, quiet);
|
||||
bs2 = bdrv_new_open("image_2", filename2, fmt2, flags, true, quiet);
|
||||
if (!bs2) {
|
||||
error_report("Can't open file %s", filename2);
|
||||
ret = 2;
|
||||
@ -1359,7 +1359,7 @@ static int img_convert(int argc, char **argv)
|
||||
|
||||
total_sectors = 0;
|
||||
for (bs_i = 0; bs_i < bs_n; bs_i++) {
|
||||
char *id = bs_n > 1 ? g_strdup_printf("source %d", bs_i)
|
||||
char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i)
|
||||
: g_strdup("source");
|
||||
bs[bs_i] = bdrv_new_open(id, argv[optind + bs_i], fmt, src_flags,
|
||||
true, quiet);
|
||||
|
@ -773,7 +773,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
} while (state != TERMINATED);
|
||||
|
||||
bdrv_close(bs);
|
||||
bdrv_unref(bs);
|
||||
if (sockpath) {
|
||||
unlink(sockpath);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
stub-obj-y += arch-query-cpu-def.o
|
||||
stub-obj-y += bdrv-commit-all.o
|
||||
stub-obj-y += blockdev.o
|
||||
stub-obj-y += chr-baum-init.o
|
||||
stub-obj-y += chr-msmouse.o
|
||||
stub-obj-y += chr-testdev.o
|
||||
|
12
stubs/blockdev.c
Normal file
12
stubs/blockdev.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <assert.h>
|
||||
#include "sysemu/blockdev.h"
|
||||
|
||||
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void drive_info_del(DriveInfo *dinfo)
|
||||
{
|
||||
assert(!dinfo);
|
||||
}
|
@ -198,6 +198,29 @@ run_qemu -drive file.driver=nbd
|
||||
run_qemu -drive file.driver=raw
|
||||
run_qemu -drive foo=bar
|
||||
|
||||
echo
|
||||
echo === Specifying both an option and its legacy alias ===
|
||||
echo
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",iops=1234,throttling.iops-total=5678
|
||||
run_qemu -drive file="$TEST_IMG",iops_rd=1234,throttling.iops-read=5678
|
||||
run_qemu -drive file="$TEST_IMG",iops_wr=1234,throttling.iops-write=5678
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",bps=1234,throttling.bps-total=5678
|
||||
run_qemu -drive file="$TEST_IMG",bps_rd=1234,throttling.bps-read=5678
|
||||
run_qemu -drive file="$TEST_IMG",bps_wr=1234,throttling.bps-write=5678
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",iops_max=1234,throttling.iops-total-max=5678
|
||||
run_qemu -drive file="$TEST_IMG",iops_rd_max=1234,throttling.iops-read-max=5678
|
||||
run_qemu -drive file="$TEST_IMG",iops_wr_max=1234,throttling.iops-write-max=5678
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",bps_max=1234,throttling.bps-total-max=5678
|
||||
run_qemu -drive file="$TEST_IMG",bps_rd_max=1234,throttling.bps-read-max=5678
|
||||
run_qemu -drive file="$TEST_IMG",bps_wr_max=1234,throttling.bps-write-max=5678
|
||||
|
||||
run_qemu -drive file="$TEST_IMG",iops_size=1234,throttling.iops-size=5678
|
||||
run_qemu -drive file="$TEST_IMG",readonly=on,read-only=off
|
||||
|
||||
echo
|
||||
echo === Parsing protocol from file name ===
|
||||
echo
|
||||
|
@ -274,6 +274,51 @@ Testing: -drive foo=bar
|
||||
QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file
|
||||
|
||||
|
||||
=== Specifying both an option and its legacy alias ===
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops=1234,throttling.iops-total=5678: 'throttling.iops-total' and its alias 'iops' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd=1234,throttling.iops-read=5678: 'throttling.iops-read' and its alias 'iops_rd' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr=1234,throttling.iops-write=5678: 'throttling.iops-write' and its alias 'iops_wr' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps=1234,throttling.bps-total=5678: 'throttling.bps-total' and its alias 'bps' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd=1234,throttling.bps-read=5678: 'throttling.bps-read' and its alias 'bps_rd' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr=1234,throttling.bps-write=5678: 'throttling.bps-write' and its alias 'bps_wr' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_max=1234,throttling.iops-total-max=5678: 'throttling.iops-total-max' and its alias 'iops_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_rd_max=1234,throttling.iops-read-max=5678: 'throttling.iops-read-max' and its alias 'iops_rd_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_wr_max=1234,throttling.iops-write-max=5678: 'throttling.iops-write-max' and its alias 'iops_wr_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_max=1234,throttling.bps-total-max=5678: 'throttling.bps-total-max' and its alias 'bps_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_rd_max=1234,throttling.bps-read-max=5678: 'throttling.bps-read-max' and its alias 'bps_rd_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,bps_wr_max=1234,throttling.bps-write-max=5678: 'throttling.bps-write-max' and its alias 'bps_wr_max' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,iops_size=1234,throttling.iops-size=5678: 'throttling.iops-size' and its alias 'iops_size' can't be used at the same time
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off
|
||||
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,readonly=on,read-only=off: 'read-only' and its alias 'readonly' can't be used at the same time
|
||||
|
||||
|
||||
=== Parsing protocol from file name ===
|
||||
|
||||
Testing: -hda foo:bar
|
||||
|
@ -20,7 +20,7 @@ QMP_VERSION
|
||||
{"return": {}}
|
||||
{"return": {}}
|
||||
{"error": {"class": "GenericError", "desc": "Device with id 'disk' already exists"}}
|
||||
{"error": {"class": "GenericError", "desc": "Device with node-name 'test-node' already exists"}}
|
||||
{"error": {"class": "GenericError", "desc": "Device name 'test-node' conflicts with an existing node name"}}
|
||||
main-loop: WARNING: I/O thread spun for 1000 iterations
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: node-name=disk is conflicting with a device id"}}
|
||||
{"error": {"class": "GenericError", "desc": "could not open disk image disk2: Duplicate node name"}}
|
||||
|
@ -376,10 +376,16 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
|
||||
echo $id >>$tmp.list
|
||||
else
|
||||
# oops
|
||||
echo "$id - unknown test, ignored"
|
||||
if [ "$start" == "$end" -a "$id" == "$end" ]
|
||||
then
|
||||
echo "$id - unknown test"
|
||||
exit 1
|
||||
else
|
||||
echo "$id - unknown test, ignored"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done || exit 1
|
||||
fi
|
||||
|
||||
done
|
||||
|
@ -641,7 +641,7 @@ QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int id_wellformed(const char *id)
|
||||
int qemu_opts_id_wellformed(const char *id)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -662,7 +662,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
||||
QemuOpts *opts = NULL;
|
||||
|
||||
if (id) {
|
||||
if (!id_wellformed(id)) {
|
||||
if (!qemu_opts_id_wellformed(id)) {
|
||||
error_set(errp,QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
|
||||
#if 0 /* conversion from qerror_report() to error_set() broke this: */
|
||||
error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n");
|
||||
|
Loading…
Reference in New Issue
Block a user