diff --git a/block.c b/block.c index a857913fc2..c5a251c57e 100644 --- a/block.c +++ b/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); } diff --git a/block/blkdebug.c b/block/blkdebug.c index ced0b600f9..f8fbb0f3d4 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -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: diff --git a/block/vpc.c b/block/vpc.c index 4947369d48..e08144a76e 100644 --- a/block/vpc.c +++ b/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); diff --git a/blockdev.c b/blockdev.c index b361fbb964..ad436488b7 100644 --- a/blockdev.c +++ b/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) { diff --git a/docs/blkdebug.txt b/docs/blkdebug.txt new file mode 100644 index 0000000000..5dde07234f --- /dev/null +++ b/docs/blkdebug.txt @@ -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. diff --git a/include/qemu/option.h b/include/qemu/option.h index 59bea759a2..945347cc8f 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -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); diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10c68..abec381049 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -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 */ diff --git a/qemu-img.c b/qemu-img.c index dbf0904dc0..ea4bbae546 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -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); diff --git a/qemu-nbd.c b/qemu-nbd.c index de9963f8fb..fa603382d4 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -773,7 +773,7 @@ int main(int argc, char **argv) } } while (state != TERMINATED); - bdrv_close(bs); + bdrv_unref(bs); if (sockpath) { unlink(sockpath); } diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 5e347d04bd..c0b1f6a320 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -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 diff --git a/stubs/blockdev.c b/stubs/blockdev.c new file mode 100644 index 0000000000..5d0a79c3a6 --- /dev/null +++ b/stubs/blockdev.c @@ -0,0 +1,12 @@ +#include +#include "sysemu/blockdev.h" + +DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) +{ + return NULL; +} + +void drive_info_del(DriveInfo *dinfo) +{ + assert(!dinfo); +} diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index a41334e022..11c858f27d 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -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 diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index a3f28209c8..2c7e808765 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -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 diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index 7fbee3ff5e..75a54e0c36 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -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"}} diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 89c6dde263..9e12bec2bf 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -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 diff --git a/util/qemu-option.c b/util/qemu-option.c index 6dc27ce04f..0cf9960fc5 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -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");