From 23f7fcb2956cc094cae008100cb1cb1fe505d00a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 15 Mar 2016 14:43:14 +0100 Subject: [PATCH 01/29] block: Fix qemu_root_bds_opts.head initialisation Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- blockdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 322ca03908..e7b8676495 100644 --- a/blockdev.c +++ b/blockdev.c @@ -4221,7 +4221,7 @@ QemuOptsList qemu_common_drive_opts = { static QemuOptsList qemu_root_bds_opts = { .name = "root-bds", - .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), + .head = QTAILQ_HEAD_INITIALIZER(qemu_root_bds_opts.head), .desc = { { .name = "discard", From f8746fb804dad4858796363adb06c56543111062 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 Mar 2016 11:14:31 +0100 Subject: [PATCH 02/29] block: Fix memory leak in hmp_drive_add_node() hmp_drive_add_node() leaked qdict in the error path when no node-name is specified. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- blockdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/blockdev.c b/blockdev.c index e7b8676495..50410bf328 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3898,6 +3898,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr) qdict = qemu_opts_to_qdict(opts, NULL); if (!qdict_get_try_str(qdict, "node-name")) { + QDECREF(qdict); error_report("'node-name' needs to be specified"); goto out; } From da27a00e274e0ea4cc3ffb4ffad1a92aef8bb15a Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:29 +0100 Subject: [PATCH 03/29] monitor: Use BB list for BB name completion Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- monitor.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monitor.c b/monitor.c index 894f862dd3..4c02f0f18f 100644 --- a/monitor.c +++ b/monitor.c @@ -42,6 +42,7 @@ #include "ui/console.h" #include "ui/input.h" #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include "audio/audio.h" #include "disas/disas.h" #include "sysemu/balloon.h" @@ -3483,7 +3484,7 @@ static void monitor_find_completion_by_table(Monitor *mon, int i; const char *ptype, *str, *name; const mon_cmd_t *cmd; - BlockDriverState *bs; + BlockBackend *blk = NULL; if (nb_args <= 1) { /* command completion */ @@ -3538,8 +3539,8 @@ static void monitor_find_completion_by_table(Monitor *mon, case 'B': /* block device name completion */ readline_set_completion_index(mon->rs, strlen(str)); - for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { - name = bdrv_get_device_name(bs); + while ((blk = blk_next(blk)) != NULL) { + name = blk_name(blk); if (str[0] == '\0' || !strncmp(name, str, strlen(str))) { readline_add_completion(mon->rs, name); From 74d1b8fc27a3dab5743b1fc5ea567640e2acf69b Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:30 +0100 Subject: [PATCH 04/29] block: Use blk_next() in block-backend.c Instead of iterating directly through blk_backends, we can use blk_next() instead. This gives us some abstraction from the list itself which we can use to rename it, for example. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 03e71b4368..7a04e10a01 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -227,9 +227,9 @@ void blk_unref(BlockBackend *blk) void blk_remove_all_bs(void) { - BlockBackend *blk; + BlockBackend *blk = NULL; - QTAILQ_FOREACH(blk, &blk_backends, link) { + while ((blk = blk_next(blk)) != NULL) { AioContext *ctx = blk_get_aio_context(blk); aio_context_acquire(ctx); @@ -271,10 +271,10 @@ const char *blk_name(BlockBackend *blk) */ BlockBackend *blk_by_name(const char *name) { - BlockBackend *blk; + BlockBackend *blk = NULL; assert(name); - QTAILQ_FOREACH(blk, &blk_backends, link) { + while ((blk = blk_next(blk)) != NULL) { if (!strcmp(name, blk->name)) { return blk; } @@ -332,9 +332,9 @@ DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) */ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) { - BlockBackend *blk; + BlockBackend *blk = NULL; - QTAILQ_FOREACH(blk, &blk_backends, link) { + while ((blk = blk_next(blk)) != NULL) { if (blk->legacy_dinfo == dinfo) { return blk; } From 1393f21270d707dfe0af3206b8757887c7b5c1ec Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:31 +0100 Subject: [PATCH 05/29] block: Add blk_commit_all() Later, we will remove bdrv_commit_all() and move its contents here, and in order to replace bdrv_commit_all() calls by calls to blk_commit_all() before doing so, we need to add it as an alias now. Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/block-backend.c | 5 +++++ include/sysemu/block-backend.h | 1 + 2 files changed, 6 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 7a04e10a01..e75b8febb0 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1329,3 +1329,8 @@ BlockBackendRootState *blk_get_root_state(BlockBackend *blk) { return &blk->root_state; } + +int blk_commit_all(void) +{ + return bdrv_commit_all(); +} diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 00d69baa07..84612cefc8 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -127,6 +127,7 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors); int blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_flush_all(void); +int blk_commit_all(void); void blk_drain(BlockBackend *blk); void blk_drain_all(void); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, From da31d594cf971795c1150ef1f5bdb01790b1b1b9 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:32 +0100 Subject: [PATCH 06/29] block: Use blk_{commit,flush}_all() consistently Replace bdrv_commmit_all() and bdrv_flush_all() by their BlockBackend equivalents. Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 2 +- cpus.c | 5 +++-- qemu-char.c | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/blockdev.c b/blockdev.c index 50410bf328..5be7d4bbd0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1173,7 +1173,7 @@ void hmp_commit(Monitor *mon, const QDict *qdict) int ret; if (!strcmp(device, "all")) { - ret = bdrv_commit_all(); + ret = blk_commit_all(); } else { BlockDriverState *bs; AioContext *aio_context; diff --git a/cpus.c b/cpus.c index 4052be525f..23cf7aad76 100644 --- a/cpus.c +++ b/cpus.c @@ -29,6 +29,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #include "exec/gdbstub.h" #include "sysemu/dma.h" #include "sysemu/kvm.h" @@ -734,7 +735,7 @@ static int do_vm_stop(RunState state) } bdrv_drain_all(); - ret = bdrv_flush_all(); + ret = blk_flush_all(); return ret; } @@ -1433,7 +1434,7 @@ int vm_stop_force_state(RunState state) bdrv_drain_all(); /* Make sure to return an error if the flush in a previous vm_stop() * failed. */ - return bdrv_flush_all(); + return blk_flush_all(); } } diff --git a/qemu-char.c b/qemu-char.c index 0a14e57839..bfcf80dd10 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -25,6 +25,7 @@ #include "qemu-common.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "sysemu/char.h" @@ -628,7 +629,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) break; } case 's': - bdrv_commit_all(); + blk_commit_all(); break; case 'b': qemu_chr_be_event(chr, CHR_EVENT_BREAK); From a55448b3681a880b77eaefe8b2c42912000cb481 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:33 +0100 Subject: [PATCH 07/29] qapi: Drop QERR_UNKNOWN_BLOCK_FORMAT_FEATURE Just specifying a custom string is simpler in basically all places that used it, and in addition, specifying the BB or node name is something we generally do not do in other error messages when opening a BDS, so we should not do it here. This changes the output for iotest 036 (to the better, in my opinion), so the reference output needs to be changed accordingly. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/qcow.c | 6 +----- block/qcow2.c | 24 +++++------------------- block/qed.c | 7 ++----- block/vmdk.c | 7 ++----- include/qapi/qmp/qerror.h | 3 --- tests/qemu-iotests/036.out | 16 ++++++++-------- 6 files changed, 18 insertions(+), 45 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 2fd5ee65d4..c09cb7989a 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -121,11 +121,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (header.version != QCOW_VERSION) { - char version[64]; - snprintf(version, sizeof(version), "QCOW version %" PRIu32, - header.version); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "qcow", version); + error_setg(errp, "Unsupported qcow version %" PRIu32, header.version); ret = -ENOTSUP; goto fail; } diff --git a/block/qcow2.c b/block/qcow2.c index 1ce6264011..3fd664b9ca 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -198,22 +198,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } -static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs, - Error **errp, const char *fmt, ...) -{ - char msg[64]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "qcow2", msg); -} - -static void report_unsupported_feature(BlockDriverState *bs, - Error **errp, Qcow2Feature *table, uint64_t mask) +static void report_unsupported_feature(Error **errp, Qcow2Feature *table, + uint64_t mask) { char *features = g_strdup(""); char *old; @@ -238,7 +224,7 @@ static void report_unsupported_feature(BlockDriverState *bs, g_free(old); } - report_unsupported(bs, errp, "%s", features); + error_setg(errp, "Unsupported qcow2 feature(s): %s", features); g_free(features); } @@ -855,7 +841,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (header.version < 2 || header.version > 3) { - report_unsupported(bs, errp, "QCOW version %" PRIu32, header.version); + error_setg(errp, "Unsupported qcow2 version %" PRIu32, header.version); ret = -ENOTSUP; goto fail; } @@ -935,7 +921,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, void *feature_table = NULL; qcow2_read_extensions(bs, header.header_length, ext_end, &feature_table, NULL); - report_unsupported_feature(bs, errp, feature_table, + report_unsupported_feature(errp, feature_table, s->incompatible_features & ~QCOW2_INCOMPAT_MASK); ret = -ENOTSUP; diff --git a/block/qed.c b/block/qed.c index 5a58856ecd..225c6a5a80 100644 --- a/block/qed.c +++ b/block/qed.c @@ -400,11 +400,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } if (s->header.features & ~QED_FEATURE_MASK) { /* image uses unsupported feature bits */ - char buf[64]; - snprintf(buf, sizeof(buf), "%" PRIx64, - s->header.features & ~QED_FEATURE_MASK); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "QED", buf); + error_setg(errp, "Unsupported QED features: %" PRIx64, + s->header.features & ~QED_FEATURE_MASK); return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { diff --git a/block/vmdk.c b/block/vmdk.c index 23bd57e20e..2f8e6cf1c8 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -661,11 +661,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, compressed = le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE; if (le32_to_cpu(header.version) > 3) { - char buf[64]; - snprintf(buf, sizeof(buf), "VMDK version %" PRId32, - le32_to_cpu(header.version)); - error_setg(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bdrv_get_device_or_node_name(bs), "vmdk", buf); + error_setg(errp, "Unsupported VMDK version %" PRIu32, + le32_to_cpu(header.version)); return -ENOTSUP; } else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR) && !compressed) { diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index f60149978a..d08652aaa5 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -100,9 +100,6 @@ #define QERR_UNDEFINED_ERROR \ "An undefined error has occurred" -#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ - "'%s' uses a %s feature which is not supported by this qemu version: %s" - #define QERR_UNSUPPORTED \ "this feature or command is not currently supported" diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index f443635b25..9b009b8c15 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -22,18 +22,18 @@ autoclear_features 0x0 refcount_order 4 header_length 104 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Unknown incompatible feature: 8000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Unknown incompatible feature: 8000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature === Image with multiple incompatible feature bits === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Unknown incompatible feature: e000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: 6000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: Test feature, Unknown incompatible feature: c000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test1, test2, Unknown incompatible feature: 8000000000000000 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test1, test2, test3 -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'image' uses a IMGFMT feature which is not supported by this qemu version: test2, Unknown incompatible feature: a000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Unknown incompatible feature: e000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature, Unknown incompatible feature: 6000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature, Unknown incompatible feature: c000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test1, test2, Unknown incompatible feature: 8000000000000000 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test1, test2, test3 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): test2, Unknown incompatible feature: a000000000000000 === Create image with unknown autoclear feature bit === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 From d0e46a55776a59c8fdbb75620e7c5fcf0d6aa029 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:34 +0100 Subject: [PATCH 08/29] block: Drop BB name from bad option error The information which BB is concerned does not seem useful enough to justify its existence in most other place (which may be related to qemu printing the -drive parameter in question anyway, and for blockdev-add the attribution is naturally unambiguous). Furthermore, as of a future patch, bdrv_get_device_name(bs) will always return the empty string before bdrv_open_inherit() returns. Therefore, just dropping that information seems to be the best course of action. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block.c | 6 +++--- tests/qemu-iotests/051.out | 8 ++++---- tests/qemu-iotests/051.pc.out | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/block.c b/block.c index 59a18a3a66..582a9e8ce3 100644 --- a/block.c +++ b/block.c @@ -1671,9 +1671,9 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, error_setg(errp, "Block protocol '%s' doesn't support the option " "'%s'", drv->format_name, entry->key); } else { - error_setg(errp, "Block format '%s' used by device '%s' doesn't " - "support the option '%s'", drv->format_name, - bdrv_get_device_name(bs), entry->key); + error_setg(errp, + "Block format '%s' does not support the option '%s'", + drv->format_name, entry->key); } ret = -EINVAL; diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 0f8a8d3562..c1291ff317 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -5,16 +5,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ === Unknown option === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' === Unknown protocol option === diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index 85fc05d05a..73cc15adeb 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -5,16 +5,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ === Unknown option === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' used by device 'drive0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo,if=none,id=drive0: Block format 'qcow2' does not support the option 'unknown_opt' === Unknown protocol option === From 9492b0b928ca23e9537c1c601a0dc9a00f65c330 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:35 +0100 Subject: [PATCH 09/29] blockdev: Rename blk_backends The blk_backends list does not contain all BlockBackends but only the ones which are referenced by the monitor, and that is not necessarily true for every BlockBackend. Rename the list to monitor_block_backends to make that fact clear. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index e75b8febb0..3bb2a6a097 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -29,7 +29,7 @@ struct BlockBackend { int refcnt; BlockDriverState *bs; DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ - QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */ + QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ void *dev; /* attached device model, if any */ /* TODO change to DeviceState when all users are qdevified */ @@ -69,9 +69,10 @@ static const AIOCBInfo block_backend_aiocb_info = { static void drive_info_del(DriveInfo *dinfo); -/* All the BlockBackends (except for hidden ones) */ -static QTAILQ_HEAD(, BlockBackend) blk_backends = - QTAILQ_HEAD_INITIALIZER(blk_backends); +/* All BlockBackends referenced by the monitor and which are iterated through by + * blk_next() */ +static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = + QTAILQ_HEAD_INITIALIZER(monitor_block_backends); /* * Create a new BlockBackend with @name, with a reference count of one. @@ -105,7 +106,7 @@ BlockBackend *blk_new(const char *name, Error **errp) blk->refcnt = 1; notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); - QTAILQ_INSERT_TAIL(&blk_backends, blk, link); + QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); return blk; } @@ -178,7 +179,7 @@ static void blk_delete(BlockBackend *blk) } /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ if (blk->name[0]) { - QTAILQ_REMOVE(&blk_backends, blk, link); + QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); } g_free(blk->name); drive_info_del(blk->legacy_dinfo); @@ -241,7 +242,7 @@ void blk_remove_all_bs(void) } /* - * Return the BlockBackend after @blk. + * Return the monitor-owned BlockBackend after @blk. * If @blk is null, return the first one. * Else, return @blk's next sibling, which may be null. * @@ -252,7 +253,8 @@ void blk_remove_all_bs(void) */ BlockBackend *blk_next(BlockBackend *blk) { - return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends); + return blk ? QTAILQ_NEXT(blk, monitor_link) + : QTAILQ_FIRST(&monitor_block_backends); } /* @@ -353,7 +355,7 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) */ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) { - QTAILQ_REMOVE(&blk_backends, blk, link); + QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); blk->name[0] = 0; if (blk->bs) { bdrv_make_anon(blk->bs); From 2cf22d6a1a542b7775ba6945a0423b41325db45f Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:36 +0100 Subject: [PATCH 10/29] blockdev: Add list of all BlockBackends While monitor_block_backends contains nearly all BBs, we sometimes really need all BBs. To this end, this patch adds the block_backend list. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/block/block-backend.c b/block/block-backend.c index 3bb2a6a097..35206be845 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -29,6 +29,7 @@ struct BlockBackend { int refcnt; BlockDriverState *bs; DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ + QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ void *dev; /* attached device model, if any */ @@ -69,6 +70,10 @@ static const AIOCBInfo block_backend_aiocb_info = { static void drive_info_del(DriveInfo *dinfo); +/* All BlockBackends */ +static QTAILQ_HEAD(, BlockBackend) block_backends = + QTAILQ_HEAD_INITIALIZER(block_backends); + /* All BlockBackends referenced by the monitor and which are iterated through by * blk_next() */ static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = @@ -106,7 +111,10 @@ BlockBackend *blk_new(const char *name, Error **errp) blk->refcnt = 1; notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); + + QTAILQ_INSERT_TAIL(&block_backends, blk, link); QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); + return blk; } @@ -177,11 +185,15 @@ static void blk_delete(BlockBackend *blk) g_free(blk->root_state.throttle_group); throttle_group_unref(blk->root_state.throttle_state); } + /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ if (blk->name[0]) { QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); } g_free(blk->name); + + QTAILQ_REMOVE(&block_backends, blk, link); + drive_info_del(blk->legacy_dinfo); block_acct_cleanup(&blk->stats); g_free(blk); @@ -226,11 +238,21 @@ void blk_unref(BlockBackend *blk) } } +/* + * Behaves similarly to blk_next() but iterates over all BlockBackends, even the + * ones which are hidden (i.e. are not referenced by the monitor). + */ +static BlockBackend *blk_all_next(BlockBackend *blk) +{ + return blk ? QTAILQ_NEXT(blk, link) + : QTAILQ_FIRST(&block_backends); +} + void blk_remove_all_bs(void) { BlockBackend *blk = NULL; - while ((blk = blk_next(blk)) != NULL) { + while ((blk = blk_all_next(blk)) != NULL) { AioContext *ctx = blk_get_aio_context(blk); aio_context_acquire(ctx); From e5e785500bf1ca286f9069bc13f3ed8cb2e9eb8a Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:37 +0100 Subject: [PATCH 11/29] blockdev: Separate BB name management Introduce separate functions (monitor_add_blk() and monitor_remove_blk()) which set or unset a BB name. Since the name is equivalent to the monitor's reference to a BB, adding a name the same as declaring the BB to be monitor-owned and removing it revokes this status, hence the function names. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 92 ++++++++++++++++++++++------------ include/sysemu/block-backend.h | 2 + 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 35206be845..a5b950c771 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -90,30 +90,17 @@ BlockBackend *blk_new(const char *name, Error **errp) { BlockBackend *blk; - assert(name && name[0]); - if (!id_wellformed(name)) { - error_setg(errp, "Invalid device name"); - return NULL; - } - if (blk_by_name(name)) { - error_setg(errp, "Device with id '%s' already exists", name); - return NULL; - } - if (bdrv_find_node(name)) { - error_setg(errp, - "Device name '%s' conflicts with an existing node name", - name); - return NULL; - } - blk = g_new0(BlockBackend, 1); - blk->name = g_strdup(name); blk->refcnt = 1; notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); QTAILQ_INSERT_TAIL(&block_backends, blk, link); - QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); + + if (!monitor_add_blk(blk, name, errp)) { + blk_unref(blk); + return NULL; + } return blk; } @@ -174,7 +161,10 @@ BlockBackend *blk_new_open(const char *name, const char *filename, static void blk_delete(BlockBackend *blk) { + monitor_remove_blk(blk); + assert(!blk->refcnt); + assert(!blk->name); assert(!blk->dev); if (blk->bs) { blk_remove_bs(blk); @@ -185,15 +175,7 @@ static void blk_delete(BlockBackend *blk) g_free(blk->root_state.throttle_group); throttle_group_unref(blk->root_state.throttle_state); } - - /* Avoid double-remove after blk_hide_on_behalf_of_hmp_drive_del() */ - if (blk->name[0]) { - QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); - } - g_free(blk->name); - QTAILQ_REMOVE(&block_backends, blk, link); - drive_info_del(blk->legacy_dinfo); block_acct_cleanup(&blk->stats); g_free(blk); @@ -279,14 +261,63 @@ BlockBackend *blk_next(BlockBackend *blk) : QTAILQ_FIRST(&monitor_block_backends); } +/* + * Add a BlockBackend into the list of backends referenced by the monitor, with + * the given @name acting as the handle for the monitor. + * Strictly for use by blockdev.c. + * + * @name must not be null or empty. + * + * Returns true on success and false on failure. In the latter case, an Error + * object is returned through @errp. + */ +bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) +{ + assert(!blk->name); + assert(name && name[0]); + + if (!id_wellformed(name)) { + error_setg(errp, "Invalid device name"); + return false; + } + if (blk_by_name(name)) { + error_setg(errp, "Device with id '%s' already exists", name); + return false; + } + if (bdrv_find_node(name)) { + error_setg(errp, + "Device name '%s' conflicts with an existing node name", + name); + return false; + } + + blk->name = g_strdup(name); + QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); + return true; +} + +/* + * Remove a BlockBackend from the list of backends referenced by the monitor. + * Strictly for use by blockdev.c. + */ +void monitor_remove_blk(BlockBackend *blk) +{ + if (!blk->name) { + return; + } + + QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); + g_free(blk->name); + blk->name = NULL; +} + /* * Return @blk's name, a non-null string. - * Wart: the name is empty iff @blk has been hidden with - * blk_hide_on_behalf_of_hmp_drive_del(). + * Returns an empty string iff @blk is not referenced by the monitor. */ const char *blk_name(BlockBackend *blk) { - return blk->name; + return blk->name ?: ""; } /* @@ -377,8 +408,7 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) */ void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) { - QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); - blk->name[0] = 0; + monitor_remove_blk(blk); if (blk->bs) { bdrv_make_anon(blk->bs); } diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 84612cefc8..c906c209b1 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -71,6 +71,8 @@ void blk_remove_all_bs(void); const char *blk_name(BlockBackend *blk); BlockBackend *blk_by_name(const char *name); BlockBackend *blk_next(BlockBackend *blk); +bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); +void monitor_remove_blk(BlockBackend *blk); BlockDriverState *blk_bs(BlockBackend *blk); void blk_remove_bs(BlockBackend *blk); From efaa7c4eeb7490c6f37f34fbc77e91f29363eebd Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:38 +0100 Subject: [PATCH 12/29] blockdev: Split monitor reference from BB creation Before this patch, blk_new() automatically assigned a name to the new BlockBackend and considered it referenced by the monitor. This patch removes the implicit monitor_add_blk() call from blk_new() (and consequently the monitor_remove_blk() call from blk_delete(), too) and thus blk_new() (and related functions) no longer take a BB name argument. In fact, there is only a single point where blk_new()/blk_new_open() is called and the new BB is monitor-owned, and that is in blockdev_init(). Besides thus relieving us from having to invent names for all of the BBs we use in qemu-img, this fixes a bug where qemu cannot create a new image if there already is a monitor-owned BB named "image". If a BB and its BDS tree are created in a single operation, as of this patch the BDS tree will be created before the BB is given a name (whereas it was the other way around before). This results in minor change to the output of iotest 087, whose reference output is amended accordingly. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 26 +++++------------- block/parallels.c | 2 +- block/qcow.c | 2 +- block/qcow2.c | 6 ++-- block/qed.c | 2 +- block/sheepdog.c | 4 +-- block/vdi.c | 2 +- block/vhdx.c | 2 +- block/vmdk.c | 6 ++-- block/vpc.c | 2 +- blockdev.c | 17 ++++++++++-- device-hotplug.c | 4 ++- hw/block/xen_disk.c | 2 +- include/sysemu/block-backend.h | 9 +++--- qemu-img.c | 50 ++++++++++++++-------------------- qemu-io.c | 2 +- qemu-nbd.c | 4 +-- tests/qemu-iotests/087.out | 2 +- 18 files changed, 68 insertions(+), 76 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index a5b950c771..9ed39121b5 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -80,13 +80,11 @@ static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = QTAILQ_HEAD_INITIALIZER(monitor_block_backends); /* - * Create a new BlockBackend with @name, with a reference count of one. - * @name must not be null or empty. - * Fail if a BlockBackend with this name already exists. + * Create a new BlockBackend with a reference count of one. * Store an error through @errp on failure, unless it's null. * Return the new BlockBackend on success, null on failure. */ -BlockBackend *blk_new(const char *name, Error **errp) +BlockBackend *blk_new(Error **errp) { BlockBackend *blk; @@ -94,14 +92,7 @@ BlockBackend *blk_new(const char *name, Error **errp) blk->refcnt = 1; notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); - QTAILQ_INSERT_TAIL(&block_backends, blk, link); - - if (!monitor_add_blk(blk, name, errp)) { - blk_unref(blk); - return NULL; - } - return blk; } @@ -109,12 +100,12 @@ BlockBackend *blk_new(const char *name, Error **errp) * Create a new BlockBackend with a new BlockDriverState attached. * Otherwise just like blk_new(), which see. */ -BlockBackend *blk_new_with_bs(const char *name, Error **errp) +BlockBackend *blk_new_with_bs(Error **errp) { BlockBackend *blk; BlockDriverState *bs; - blk = blk_new(name, errp); + blk = blk_new(errp); if (!blk) { return NULL; } @@ -137,14 +128,13 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp) * though, so callers of this function have to be able to specify @filename and * @flags. */ -BlockBackend *blk_new_open(const char *name, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp) +BlockBackend *blk_new_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp) { BlockBackend *blk; int ret; - blk = blk_new_with_bs(name, errp); + blk = blk_new_with_bs(errp); if (!blk) { QDECREF(options); return NULL; @@ -161,8 +151,6 @@ BlockBackend *blk_new_open(const char *name, const char *filename, static void blk_delete(BlockBackend *blk) { - monitor_remove_blk(blk); - assert(!blk->refcnt); assert(!blk->name); assert(!blk->dev); diff --git a/block/parallels.c b/block/parallels.c index 0d1a60c972..b322d05c08 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -478,7 +478,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) return ret; } - file = blk_new_open("image", filename, NULL, NULL, + file = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (file == NULL) { diff --git a/block/qcow.c b/block/qcow.c index c09cb7989a..73cf8a7081 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -793,7 +793,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) goto cleanup; } - qcow_blk = blk_new_open("image", filename, NULL, NULL, + qcow_blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (qcow_blk == NULL) { diff --git a/block/qcow2.c b/block/qcow2.c index 3fd664b9ca..5f4fea6a55 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2159,7 +2159,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, return ret; } - blk = blk_new_open("image", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { @@ -2224,7 +2224,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, */ options = qdict_new(); qdict_put(options, "driver", qstring_from_str("qcow2")); - blk = blk_new_open("image-qcow2", filename, NULL, options, + blk = blk_new_open(filename, NULL, options, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, &local_err); if (blk == NULL) { @@ -2286,7 +2286,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ options = qdict_new(); qdict_put(options, "driver", qstring_from_str("qcow2")); - blk = blk_new_open("image-flush", filename, NULL, options, + blk = blk_new_open(filename, NULL, options, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, &local_err); if (blk == NULL) { diff --git a/block/qed.c b/block/qed.c index 225c6a5a80..5b24a9783f 100644 --- a/block/qed.c +++ b/block/qed.c @@ -574,7 +574,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, return ret; } - blk = blk_new_open("image", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { diff --git a/block/sheepdog.c b/block/sheepdog.c index 06ae3bac62..a3aeae4a67 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1646,7 +1646,7 @@ static int sd_prealloc(const char *filename, Error **errp) void *buf = NULL; int ret; - blk = blk_new_open("image-prealloc", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, errp); if (blk == NULL) { @@ -1843,7 +1843,7 @@ static int sd_create(const char *filename, QemuOpts *opts, goto out; } - blk = blk_new_open("backing", backing_file, NULL, NULL, + blk = blk_new_open(backing_file, NULL, NULL, BDRV_O_PROTOCOL | BDRV_O_CACHE_WB, errp); if (blk == NULL) { ret = -EIO; diff --git a/block/vdi.c b/block/vdi.c index 662d14b74e..df9fa47db1 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -768,7 +768,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } - blk = blk_new_open("image", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { diff --git a/block/vhdx.c b/block/vhdx.c index e15020c9be..78fe56ca04 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1838,7 +1838,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } - blk = blk_new_open("image", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { diff --git a/block/vmdk.c b/block/vmdk.c index 2f8e6cf1c8..80f033835e 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1661,7 +1661,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, goto exit; } - blk = blk_new_open("extent", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { @@ -1946,7 +1946,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } - blk = blk_new_open("backing", full_backing, NULL, NULL, + blk = blk_new_open(full_backing, NULL, NULL, BDRV_O_NO_BACKING | BDRV_O_CACHE_WB, errp); g_free(full_backing); if (blk == NULL) { @@ -2018,7 +2018,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) } } - new_blk = blk_new_open("descriptor", filename, NULL, NULL, + new_blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (new_blk == NULL) { diff --git a/block/vpc.c b/block/vpc.c index 0d1524d6f6..8435205a0c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -888,7 +888,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) goto out; } - blk = blk_new_open("image", filename, NULL, NULL, + blk = blk_new_open(filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (blk == NULL) { diff --git a/blockdev.c b/blockdev.c index 5be7d4bbd0..d0e3d9c41a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -147,6 +147,7 @@ void blockdev_auto_del(BlockBackend *blk) DriveInfo *dinfo = blk_legacy_dinfo(blk); if (dinfo && dinfo->auto_del) { + monitor_remove_blk(blk); blk_unref(blk); } } @@ -561,7 +562,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, if ((!file || !*file) && !qdict_size(bs_opts)) { BlockBackendRootState *blk_rs; - blk = blk_new(qemu_opts_id(opts), errp); + blk = blk_new(errp); if (!blk) { goto early_err; } @@ -597,8 +598,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, bdrv_flags |= BDRV_O_INACTIVE; } - blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, - errp); + blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp); if (!blk) { goto err_no_bs_opts; } @@ -630,6 +630,12 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, blk_set_on_error(blk, on_read_error, on_write_error); + if (!monitor_add_blk(blk, qemu_opts_id(opts), errp)) { + blk_unref(blk); + blk = NULL; + goto err_no_bs_opts; + } + err_no_bs_opts: qemu_opts_del(opts); QDECREF(interval_dict); @@ -2859,6 +2865,8 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) blk_remove_bs(blk); } + monitor_remove_blk(blk); + /* if we have a device attached to this BlockDriverState * then we need to make the drive anonymous until the device * can be removed. If this is a drive with no device backing @@ -3976,6 +3984,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) if (bs && bdrv_key_required(bs)) { if (blk) { + monitor_remove_blk(blk); blk_unref(blk); } else { QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); @@ -4005,6 +4014,7 @@ void qmp_x_blockdev_del(bool has_id, const char *id, } if (has_id) { + /* blk_by_name() never returns a BB that is not owned by the monitor */ blk = blk_by_name(id); if (!blk) { error_setg(errp, "Cannot find block backend %s", id); @@ -4052,6 +4062,7 @@ void qmp_x_blockdev_del(bool has_id, const char *id, } if (blk) { + monitor_remove_blk(blk); blk_unref(blk); } else { QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); diff --git a/device-hotplug.c b/device-hotplug.c index 3e5cdaad10..126f73c676 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -84,6 +84,8 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) err: if (dinfo) { - blk_unref(blk_by_legacy_dinfo(dinfo)); + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + monitor_remove_blk(blk); + blk_unref(blk); } } diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 7bd5bdefd3..635328fa69 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -917,7 +917,7 @@ static int blk_connect(struct XenDevice *xendev) /* setup via xenbus -> create new block driver instance */ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); - blkdev->blk = blk_new_open(blkdev->dev, blkdev->filename, NULL, options, + blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { xen_be_printf(&blkdev->xendev, 0, "error: %s\n", diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index c906c209b1..5edc427030 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -59,11 +59,10 @@ typedef struct BlockDevOps { void (*resize_cb)(void *opaque); } BlockDevOps; -BlockBackend *blk_new(const char *name, Error **errp); -BlockBackend *blk_new_with_bs(const char *name, Error **errp); -BlockBackend *blk_new_open(const char *name, const char *filename, - const char *reference, QDict *options, int flags, - Error **errp); +BlockBackend *blk_new(Error **errp); +BlockBackend *blk_new_with_bs(Error **errp); +BlockBackend *blk_new_open(const char *filename, const char *reference, + QDict *options, int flags, Error **errp); int blk_get_refcnt(BlockBackend *blk); void blk_ref(BlockBackend *blk); void blk_unref(BlockBackend *blk); diff --git a/qemu-img.c b/qemu-img.c index 3103150717..29eae2a24b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -245,8 +245,7 @@ static int img_open_password(BlockBackend *blk, const char *filename, } -static BlockBackend *img_open_opts(const char *id, - const char *optstr, +static BlockBackend *img_open_opts(const char *optstr, QemuOpts *opts, int flags, bool require_io, bool quiet) { @@ -254,7 +253,7 @@ static BlockBackend *img_open_opts(const char *id, Error *local_err = NULL; BlockBackend *blk; options = qemu_opts_to_qdict(opts, NULL); - blk = blk_new_open(id, NULL, NULL, options, flags, &local_err); + blk = blk_new_open(NULL, NULL, options, flags, &local_err); if (!blk) { error_reportf_err(local_err, "Could not open '%s'", optstr); return NULL; @@ -267,7 +266,7 @@ static BlockBackend *img_open_opts(const char *id, return blk; } -static BlockBackend *img_open_file(const char *id, const char *filename, +static BlockBackend *img_open_file(const char *filename, const char *fmt, int flags, bool require_io, bool quiet) { @@ -280,7 +279,7 @@ static BlockBackend *img_open_file(const char *id, const char *filename, qdict_put(options, "driver", qstring_from_str(fmt)); } - blk = blk_new_open(id, filename, NULL, options, flags, &local_err); + blk = blk_new_open(filename, NULL, options, flags, &local_err); if (!blk) { error_reportf_err(local_err, "Could not open '%s': ", filename); return NULL; @@ -294,8 +293,7 @@ static BlockBackend *img_open_file(const char *id, const char *filename, } -static BlockBackend *img_open(const char *id, - bool image_opts, +static BlockBackend *img_open(bool image_opts, const char *filename, const char *fmt, int flags, bool require_io, bool quiet) @@ -312,9 +310,9 @@ static BlockBackend *img_open(const char *id, if (!opts) { return NULL; } - blk = img_open_opts(id, filename, opts, flags, true, quiet); + blk = img_open_opts(filename, opts, flags, true, quiet); } else { - blk = img_open_file(id, filename, fmt, flags, true, quiet); + blk = img_open_file(filename, fmt, flags, true, quiet); } return blk; } @@ -686,7 +684,7 @@ static int img_check(int argc, char **argv) return 1; } - blk = img_open("image", image_opts, filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, true, quiet); if (!blk) { return 1; } @@ -878,7 +876,7 @@ static int img_commit(int argc, char **argv) return 1; } - blk = img_open("image", image_opts, filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, true, quiet); if (!blk) { return 1; } @@ -1212,13 +1210,13 @@ static int img_compare(int argc, char **argv) goto out3; } - blk1 = img_open("image_1", image_opts, filename1, fmt1, flags, true, quiet); + blk1 = img_open(image_opts, filename1, fmt1, flags, true, quiet); if (!blk1) { ret = 2; goto out3; } - blk2 = img_open("image_2", image_opts, filename2, fmt2, flags, true, quiet); + blk2 = img_open(image_opts, filename2, fmt2, flags, true, quiet); if (!blk2) { ret = 2; goto out2; @@ -1899,11 +1897,8 @@ 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) - : g_strdup("source"); - blk[bs_i] = img_open(id, image_opts, argv[optind + bs_i], + blk[bs_i] = img_open(image_opts, argv[optind + bs_i], fmt, src_flags, true, quiet); - g_free(id); if (!blk[bs_i]) { ret = -1; goto out; @@ -2048,8 +2043,7 @@ static int img_convert(int argc, char **argv) * the bdrv_create() call which takes different params. * Not critical right now, so fix can wait... */ - out_blk = img_open_file("target", out_filename, - out_fmt, flags, true, quiet); + out_blk = img_open_file(out_filename, out_fmt, flags, true, quiet); if (!out_blk) { ret = -1; goto out; @@ -2240,7 +2234,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, } g_hash_table_insert(filenames, (gpointer)filename, NULL); - blk = img_open("image", image_opts, filename, fmt, + blk = img_open(image_opts, filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false); if (!blk) { @@ -2572,8 +2566,7 @@ static int img_map(int argc, char **argv) return 1; } - blk = img_open("image", image_opts, filename, fmt, - BDRV_O_FLAGS, true, false); + blk = img_open(image_opts, filename, fmt, BDRV_O_FLAGS, true, false); if (!blk) { return 1; } @@ -2718,8 +2711,7 @@ static int img_snapshot(int argc, char **argv) } /* Open the image */ - blk = img_open("image", image_opts, filename, NULL, - bdrv_oflags, true, quiet); + blk = img_open(image_opts, filename, NULL, bdrv_oflags, true, quiet); if (!blk) { return 1; } @@ -2890,7 +2882,7 @@ static int img_rebase(int argc, char **argv) * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - blk = img_open("image", image_opts, filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, true, quiet); if (!blk) { ret = -1; goto out; @@ -2916,7 +2908,7 @@ static int img_rebase(int argc, char **argv) } bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open("old_backing", backing_name, NULL, + blk_old_backing = blk_new_open(backing_name, NULL, options, src_flags, &local_err); if (!blk_old_backing) { error_reportf_err(local_err, @@ -2933,7 +2925,7 @@ static int img_rebase(int argc, char **argv) options = NULL; } - blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL, + blk_new_backing = blk_new_open(out_baseimg, NULL, options, src_flags, &local_err); if (!blk_new_backing) { error_reportf_err(local_err, @@ -3227,7 +3219,7 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - blk = img_open("image", image_opts, filename, fmt, + blk = img_open(image_opts, filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); if (!blk) { ret = -1; @@ -3387,7 +3379,7 @@ static int img_amend(int argc, char **argv) goto out; } - blk = img_open("image", image_opts, filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, true, quiet); if (!blk) { ret = -1; goto out; diff --git a/qemu-io.c b/qemu-io.c index 8c31257ac4..d7c2f26bbb 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -61,7 +61,7 @@ static int openfile(char *name, int flags, QDict *opts) return 1; } - qemuio_blk = blk_new_open("hda", name, NULL, opts, flags, &local_err); + qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); if (!qemuio_blk) { error_reportf_err(local_err, "can't open%s%s: ", name ? " device " : "", name ?: ""); diff --git a/qemu-nbd.c b/qemu-nbd.c index a5c1d95344..d5f847386f 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -831,13 +831,13 @@ int main(int argc, char **argv) } options = qemu_opts_to_qdict(opts, NULL); qemu_opts_reset(&file_opts); - blk = blk_new_open("hda", NULL, NULL, options, flags, &local_err); + blk = blk_new_open(NULL, NULL, options, flags, &local_err); } else { if (fmt) { options = qdict_new(); qdict_put(options, "driver", qstring_from_str(fmt)); } - blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err); + blk = blk_new_open(srcpath, NULL, options, flags, &local_err); } if (!blk) { diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index 7d62cd5840..d0662f95e8 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -21,7 +21,7 @@ QMP_VERSION {"error": {"class": "GenericError", "desc": "Device name 'test-node' conflicts with an existing node name"}} {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}} {"error": {"class": "GenericError", "desc": "Duplicate node name"}} -{"error": {"class": "GenericError", "desc": "node-name=disk3 is conflicting with a device id"}} +{"error": {"class": "GenericError", "desc": "Device name 'disk3' conflicts with an existing node name"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} From 7c735873d93ac18a6f06850c7ca6b3722b1b32c5 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:39 +0100 Subject: [PATCH 13/29] blockdev: Remove blk_hide_on_behalf_of_hmp_drive_del() We can basically inline it in hmp_drive_del(); monitor_remove_blk() is called already, so we just need to call bdrv_make_anon(), too. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 17 ----------------- blockdev.c | 11 ++++++----- include/sysemu/block-backend.h | 2 -- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 9ed39121b5..68f3662e7d 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -385,23 +385,6 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) abort(); } -/* - * Hide @blk. - * @blk must not have been hidden already. - * Make attached BlockDriverState, if any, anonymous. - * Once hidden, @blk is invisible to all functions that don't receive - * it as argument. For example, blk_by_name() won't return it. - * Strictly for use by do_drive_del(). - * TODO get rid of it! - */ -void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk) -{ - monitor_remove_blk(blk); - if (blk->bs) { - bdrv_make_anon(blk->bs); - } -} - /* * Disassociates the currently associated BlockDriverState from @blk. */ diff --git a/blockdev.c b/blockdev.c index d0e3d9c41a..b6d24449e3 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2865,15 +2865,16 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) blk_remove_bs(blk); } + /* Make the BlockBackend and the attached BlockDriverState anonymous */ monitor_remove_blk(blk); + if (blk_bs(blk)) { + bdrv_make_anon(blk_bs(blk)); + } - /* if we have a device attached to this BlockDriverState - * then we need to make the drive anonymous until the device - * can be removed. If this is a drive with no device backing - * then we can just get rid of the block driver state right here. + /* If this BlockBackend has a device attached to it, its refcount will be + * decremented when the device is removed; otherwise we have to do so here. */ if (blk_get_attached_dev(blk)) { - blk_hide_on_behalf_of_hmp_drive_del(blk); /* Further I/O must not pause the guest */ blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 5edc427030..60c4b070ca 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -77,8 +77,6 @@ BlockDriverState *blk_bs(BlockBackend *blk); void blk_remove_bs(BlockBackend *blk); void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs); -void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk); - void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); void blk_iostatus_enable(BlockBackend *blk); bool blk_iostatus_is_enabled(const BlockBackend *blk); From fe1a9cbc339bb54d20f1ca4c1e8788d16944d5cf Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:40 +0100 Subject: [PATCH 14/29] block: Move some bdrv_*_all() functions to BB Move bdrv_commit_all() and bdrv_flush_all() to the BlockBackend level. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block.c | 20 --------- block/block-backend.c | 44 ++++++++++++++++--- block/io.c | 20 --------- include/block/block.h | 2 - stubs/Makefile.objs | 2 +- stubs/{bdrv-commit-all.c => blk-commit-all.c} | 4 +- 6 files changed, 41 insertions(+), 51 deletions(-) rename stubs/{bdrv-commit-all.c => blk-commit-all.c} (53%) diff --git a/block.c b/block.c index 582a9e8ce3..91c006a3d3 100644 --- a/block.c +++ b/block.c @@ -2521,26 +2521,6 @@ ro_cleanup: return ret; } -int bdrv_commit_all(void) -{ - BlockDriverState *bs; - - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { - AioContext *aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - if (bs->drv && bs->backing) { - int ret = bdrv_commit(bs); - if (ret < 0) { - aio_context_release(aio_context); - return ret; - } - } - aio_context_release(aio_context); - } - return 0; -} - /* * Return values: * 0 - success diff --git a/block/block-backend.c b/block/block-backend.c index 68f3662e7d..b3c3d39db8 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -904,11 +904,6 @@ int blk_flush(BlockBackend *blk) return bdrv_flush(blk->bs); } -int blk_flush_all(void) -{ - return bdrv_flush_all(); -} - void blk_drain(BlockBackend *blk) { if (blk->bs) { @@ -1357,5 +1352,42 @@ BlockBackendRootState *blk_get_root_state(BlockBackend *blk) int blk_commit_all(void) { - return bdrv_commit_all(); + BlockBackend *blk = NULL; + + while ((blk = blk_all_next(blk)) != NULL) { + AioContext *aio_context = blk_get_aio_context(blk); + + aio_context_acquire(aio_context); + if (blk_is_inserted(blk) && blk->bs->backing) { + int ret = bdrv_commit(blk->bs); + if (ret < 0) { + aio_context_release(aio_context); + return ret; + } + } + aio_context_release(aio_context); + } + return 0; +} + +int blk_flush_all(void) +{ + BlockBackend *blk = NULL; + int result = 0; + + while ((blk = blk_all_next(blk)) != NULL) { + AioContext *aio_context = blk_get_aio_context(blk); + int ret; + + aio_context_acquire(aio_context); + if (blk_is_inserted(blk)) { + ret = blk_flush(blk); + if (ret < 0 && !result) { + result = ret; + } + } + aio_context_release(aio_context); + } + + return result; } diff --git a/block/io.c b/block/io.c index a69bfc4197..5f9b6d623d 100644 --- a/block/io.c +++ b/block/io.c @@ -1445,26 +1445,6 @@ int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, BDRV_REQ_ZERO_WRITE | flags); } -int bdrv_flush_all(void) -{ - BlockDriverState *bs = NULL; - int result = 0; - - while ((bs = bdrv_next(bs))) { - AioContext *aio_context = bdrv_get_aio_context(bs); - int ret; - - aio_context_acquire(aio_context); - ret = bdrv_flush(bs); - if (ret < 0 && !result) { - result = ret; - } - aio_context_release(aio_context); - } - - return result; -} - typedef struct BdrvCoGetBlockStatusData { BlockDriverState *bs; BlockDriverState *base; diff --git a/include/block/block.h b/include/block/block.h index eaa64262d9..ea5be0f46c 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -274,7 +274,6 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_refresh_limits(BlockDriverState *bs, Error **errp); int bdrv_commit(BlockDriverState *bs); -int bdrv_commit_all(void); int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt); void bdrv_register(BlockDriver *bdrv); @@ -373,7 +372,6 @@ int bdrv_inactivate_all(void); /* Ensure contents are flushed to disk. */ int bdrv_flush(BlockDriverState *bs); int coroutine_fn bdrv_co_flush(BlockDriverState *bs); -int bdrv_flush_all(void); void bdrv_close_all(void); void bdrv_drain(BlockDriverState *bs); void bdrv_drain_all(void); diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index e922de982f..9d9f1d037b 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,5 +1,5 @@ stub-obj-y += arch-query-cpu-def.o -stub-obj-y += bdrv-commit-all.o +stub-obj-y += blk-commit-all.o stub-obj-y += blockdev-close-all-bdrv-states.o stub-obj-y += clock-warp.o stub-obj-y += cpu-get-clock.o diff --git a/stubs/bdrv-commit-all.c b/stubs/blk-commit-all.c similarity index 53% rename from stubs/bdrv-commit-all.c rename to stubs/blk-commit-all.c index bf84a1d85a..c82fb7f5f8 100644 --- a/stubs/bdrv-commit-all.c +++ b/stubs/blk-commit-all.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "block/block.h" +#include "sysemu/block-backend.h" -int bdrv_commit_all(void) +int blk_commit_all(void) { return 0; } From 262b4e8f74dfd5ea9159aa98dcd705158b12e8ed Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:41 +0100 Subject: [PATCH 15/29] block: Add bdrv_next_monitor_owned() Add a function for iterating over all monitor-owned BlockDriverStates so the generic block layer can do so. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 7 +++++++ include/block/block.h | 1 + stubs/Makefile.objs | 1 + stubs/bdrv-next-monitor-owned.c | 8 ++++++++ 4 files changed, 17 insertions(+) create mode 100644 stubs/bdrv-next-monitor-owned.c diff --git a/blockdev.c b/blockdev.c index b6d24449e3..a5df7e7590 100644 --- a/blockdev.c +++ b/blockdev.c @@ -723,6 +723,13 @@ void blockdev_close_all_bdrv_states(void) } } +/* Iterates over the list of monitor-owned BlockDriverStates */ +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) +{ + return bs ? QTAILQ_NEXT(bs, monitor_list) + : QTAILQ_FIRST(&monitor_bdrv_states); +} + static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, Error **errp) { diff --git a/include/block/block.h b/include/block/block.h index ea5be0f46c..09272c3e77 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -412,6 +412,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); BlockDriverState *bdrv_next_node(BlockDriverState *bs); BlockDriverState *bdrv_next(BlockDriverState *bs); +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); int bdrv_is_encrypted(BlockDriverState *bs); int bdrv_key_required(BlockDriverState *bs); int bdrv_set_key(BlockDriverState *bs, const char *key); diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 9d9f1d037b..b6d1e650db 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,4 +1,5 @@ stub-obj-y += arch-query-cpu-def.o +stub-obj-y += bdrv-next-monitor-owned.o stub-obj-y += blk-commit-all.o stub-obj-y += blockdev-close-all-bdrv-states.o stub-obj-y += clock-warp.o diff --git a/stubs/bdrv-next-monitor-owned.c b/stubs/bdrv-next-monitor-owned.c new file mode 100644 index 0000000000..2acf6c3ec0 --- /dev/null +++ b/stubs/bdrv-next-monitor-owned.c @@ -0,0 +1,8 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "block/block.h" + +BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) +{ + return NULL; +} From 981f4f578e6a0dfb2d02384941ea17f7ea1a14ec Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:42 +0100 Subject: [PATCH 16/29] block: Add blk_next_root_bs() This function iterates over all BDSs attached to a BB. We are going to need it when rewriting bdrv_next() so it no longer uses bdrv_states. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/block-backend.c | 24 ++++++++++++++++++++++++ include/sysemu/block-backend.h | 1 + 2 files changed, 25 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index b3c3d39db8..7818aa2634 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -249,6 +249,30 @@ BlockBackend *blk_next(BlockBackend *blk) : QTAILQ_FIRST(&monitor_block_backends); } +/* + * Iterates over all BlockDriverStates which are attached to a BlockBackend. + * This function is for use by bdrv_next(). + * + * @bs must be NULL or a BDS that is attached to a BB. + */ +BlockDriverState *blk_next_root_bs(BlockDriverState *bs) +{ + BlockBackend *blk; + + if (bs) { + assert(bs->blk); + blk = bs->blk; + } else { + blk = NULL; + } + + do { + blk = blk_all_next(blk); + } while (blk && !blk->bs); + + return blk ? blk->bs : NULL; +} + /* * Add a BlockBackend into the list of backends referenced by the monitor, with * the given @name acting as the handle for the monitor. diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 60c4b070ca..d839bffeb0 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -70,6 +70,7 @@ void blk_remove_all_bs(void); const char *blk_name(BlockBackend *blk); BlockBackend *blk_by_name(const char *name); BlockBackend *blk_next(BlockBackend *blk); +BlockDriverState *blk_next_root_bs(BlockDriverState *bs); bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); void monitor_remove_blk(BlockBackend *blk); From 26260580349e0369858f03f46ebaf9e31c2a780f Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:43 +0100 Subject: [PATCH 17/29] block: Rewrite bdrv_next() Instead of using the bdrv_states list, iterate over all the BlockDriverStates attached to BlockBackends, and over all the monitor-owned BDSs afterwards (except for those attached to a BB). Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index 91c006a3d3..8f700f1212 100644 --- a/block.c +++ b/block.c @@ -2969,12 +2969,23 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) return QTAILQ_NEXT(bs, node_list); } +/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by + * the monitor or attached to a BlockBackend */ BlockDriverState *bdrv_next(BlockDriverState *bs) { - if (!bs) { - return QTAILQ_FIRST(&bdrv_states); + if (!bs || bs->blk) { + bs = blk_next_root_bs(bs); + if (bs) { + return bs; + } } - return QTAILQ_NEXT(bs, device_list); + + /* Ignore all BDSs that are attached to a BlockBackend here; they have been + * handled by the above block already */ + do { + bs = bdrv_next_monitor_owned(bs); + } while (bs && bs->blk); + return bs; } const char *bdrv_get_node_name(const BlockDriverState *bs) From 79720af640f3d23b446df4284a8f5b98efbb709d Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:44 +0100 Subject: [PATCH 18/29] block: Use bdrv_next() instead of bdrv_states There is no point in manually iterating through the bdrv_states list when there is bdrv_next(). Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/block.c b/block.c index 8f700f1212..5d848fbbc5 100644 --- a/block.c +++ b/block.c @@ -3293,10 +3293,10 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) void bdrv_invalidate_cache_all(Error **errp) { - BlockDriverState *bs; + BlockDriverState *bs = NULL; Error *local_err = NULL; - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + while ((bs = bdrv_next(bs)) != NULL) { AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3326,10 +3326,10 @@ static int bdrv_inactivate(BlockDriverState *bs) int bdrv_inactivate_all(void) { - BlockDriverState *bs; + BlockDriverState *bs = NULL; int ret; - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + while ((bs = bdrv_next(bs)) != NULL) { AioContext *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -3835,10 +3835,10 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, */ bool bdrv_is_first_non_filter(BlockDriverState *candidate) { - BlockDriverState *bs; + BlockDriverState *bs = NULL; /* walk down the bs forest recursively */ - QTAILQ_FOREACH(bs, &bdrv_states, device_list) { + while ((bs = bdrv_next(bs)) != NULL) { bool perm; /* try to recurse in this top level bs */ From 9aaf28c61dd3e3f4af125dd5d62625e4645f1958 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 16 Mar 2016 19:54:45 +0100 Subject: [PATCH 19/29] block: Remove bdrv_states list Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 31 +++---------------------------- blockdev.c | 7 ------- include/block/block.h | 1 - include/block/block_int.h | 4 ---- 4 files changed, 3 insertions(+), 40 deletions(-) diff --git a/block.c b/block.c index 5d848fbbc5..c47f99d1fa 100644 --- a/block.c +++ b/block.c @@ -55,8 +55,6 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ -struct BdrvStates bdrv_states = QTAILQ_HEAD_INITIALIZER(bdrv_states); - static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); @@ -226,10 +224,7 @@ void bdrv_register(BlockDriver *bdrv) BlockDriverState *bdrv_new_root(void) { - BlockDriverState *bs = bdrv_new(); - - QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); - return bs; + return bdrv_new(); } BlockDriverState *bdrv_new(void) @@ -2230,26 +2225,10 @@ void bdrv_close_all(void) } } -/* Note that bs->device_list.tqe_prev is initially null, - * and gets set to non-null by QTAILQ_INSERT_TAIL(). Establish - * the useful invariant "bs in bdrv_states iff bs->tqe_prev" by - * resetting it to null on remove. */ -void bdrv_device_remove(BlockDriverState *bs) -{ - QTAILQ_REMOVE(&bdrv_states, bs, device_list); - bs->device_list.tqe_prev = NULL; -} - -/* make a BlockDriverState anonymous by removing from bdrv_state and - * graph_bdrv_state list. - Also, NULL terminate the device_name to prevent double remove */ +/* make a BlockDriverState anonymous by removing from graph_bdrv_state list. + * Also, NULL terminate the device_name to prevent double remove */ void bdrv_make_anon(BlockDriverState *bs) { - /* Take care to remove bs from bdrv_states only when it's actually - * in it. */ - if (bs->device_list.tqe_prev) { - bdrv_device_remove(bs); - } if (bs->node_name[0] != '\0') { QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); } @@ -2286,10 +2265,6 @@ static void change_parent_backing_link(BlockDriverState *from, } if (from->blk) { blk_set_bs(from->blk, to); - if (!to->device_list.tqe_prev) { - QTAILQ_INSERT_BEFORE(from, to, device_list); - } - bdrv_device_remove(from); } } diff --git a/blockdev.c b/blockdev.c index a5df7e7590..85dee5787b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2426,11 +2426,6 @@ void qmp_x_blockdev_remove_medium(const char *device, Error **errp) goto out; } - /* This follows the convention established by bdrv_make_anon() */ - if (bs->device_list.tqe_prev) { - bdrv_device_remove(bs); - } - blk_remove_bs(blk); if (!blk_dev_has_tray(blk)) { @@ -2478,8 +2473,6 @@ static void qmp_blockdev_insert_anon_medium(const char *device, blk_insert_bs(blk, bs); - QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list); - if (!blk_dev_has_tray(blk)) { /* For tray-less devices, blockdev-close-tray is a no-op (or may not be * called at all); therefore, the medium needs to be pushed into the diff --git a/include/block/block.h b/include/block/block.h index 09272c3e77..ea8ed04403 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -201,7 +201,6 @@ int bdrv_create(BlockDriver *drv, const char* filename, int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); BlockDriverState *bdrv_new_root(void); BlockDriverState *bdrv_new(void); -void bdrv_device_remove(BlockDriverState *bs); void bdrv_make_anon(BlockDriverState *bs); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); void bdrv_replace_in_backing_chain(BlockDriverState *old, diff --git a/include/block/block_int.h b/include/block/block_int.h index dda5ba0927..bbf617a031 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -442,8 +442,6 @@ struct BlockDriverState { char node_name[32]; /* element of the list of named nodes building the graph */ QTAILQ_ENTRY(BlockDriverState) node_list; - /* element of the list of "drives" the guest sees */ - QTAILQ_ENTRY(BlockDriverState) device_list; /* element of the list of all BlockDriverStates (all_bdrv_states) */ QTAILQ_ENTRY(BlockDriverState) bs_list; /* element of the list of monitor-owned BDS */ @@ -501,8 +499,6 @@ extern BlockDriver bdrv_file; extern BlockDriver bdrv_raw; extern BlockDriver bdrv_qcow2; -extern QTAILQ_HEAD(BdrvStates, BlockDriverState) bdrv_states; - /** * bdrv_setup_io_funcs: * From f21d96d04b6949c1a5b4a24c73a296a1bc4bdad6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:46 +0100 Subject: [PATCH 20/29] block: Use BdrvChild in BlockBackend Signed-off-by: Kevin Wolf --- block.c | 47 +++++-- block/block-backend.c | 254 +++++++++++++++++++++++--------------- include/block/block_int.h | 5 + 3 files changed, 192 insertions(+), 114 deletions(-) diff --git a/block.c b/block.c index c47f99d1fa..b4107fcd4c 100644 --- a/block.c +++ b/block.c @@ -1175,10 +1175,9 @@ static int bdrv_fill_options(QDict **options, const char *filename, return 0; } -static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, - BlockDriverState *child_bs, - const char *child_name, - const BdrvChildRole *child_role) +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role) { BdrvChild *child = g_new(BdrvChild, 1); *child = (BdrvChild) { @@ -1187,24 +1186,43 @@ static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, .role = child_role, }; - QLIST_INSERT_HEAD(&parent_bs->children, child, next); QLIST_INSERT_HEAD(&child_bs->parents, child, next_parent); return child; } +static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role) +{ + BdrvChild *child = bdrv_root_attach_child(child_bs, child_name, child_role); + QLIST_INSERT_HEAD(&parent_bs->children, child, next); + return child; +} + static void bdrv_detach_child(BdrvChild *child) { - QLIST_REMOVE(child, next); + if (child->next.le_prev) { + QLIST_REMOVE(child, next); + child->next.le_prev = NULL; + } QLIST_REMOVE(child, next_parent); g_free(child->name); g_free(child); } -void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) +void bdrv_root_unref_child(BdrvChild *child) { BlockDriverState *child_bs; + child_bs = child->bs; + bdrv_detach_child(child); + bdrv_unref(child_bs); +} + +void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) +{ if (child == NULL) { return; } @@ -1213,9 +1231,7 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) child->bs->inherits_from = NULL; } - child_bs = child->bs; - bdrv_detach_child(child); - bdrv_unref(child_bs); + bdrv_root_unref_child(child); } /* @@ -2255,6 +2271,14 @@ static void change_parent_backing_link(BlockDriverState *from, { BdrvChild *c, *next; + if (from->blk) { + /* FIXME We bypass blk_set_bs(), so we need to make these updates + * manually. The root problem is not in this change function, but the + * existence of BlockDriverState.blk. */ + to->blk = from->blk; + from->blk = NULL; + } + QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->role != &child_backing); c->bs = to; @@ -2263,9 +2287,6 @@ static void change_parent_backing_link(BlockDriverState *from, bdrv_ref(to); bdrv_unref(from); } - if (from->blk) { - blk_set_bs(from->blk, to); - } } static void swap_feature_fields(BlockDriverState *bs_top, diff --git a/block/block-backend.c b/block/block-backend.c index 7818aa2634..5de785026b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -27,7 +27,7 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); struct BlockBackend { char *name; int refcnt; - BlockDriverState *bs; + BdrvChild *root; DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ @@ -79,6 +79,17 @@ static QTAILQ_HEAD(, BlockBackend) block_backends = static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = QTAILQ_HEAD_INITIALIZER(monitor_block_backends); +static void blk_root_inherit_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) +{ + /* We're not supposed to call this function for root nodes */ + abort(); +} + +static const BdrvChildRole child_root = { + .inherit_options = blk_root_inherit_options, +}; + /* * Create a new BlockBackend with a reference count of one. * Store an error through @errp on failure, unless it's null. @@ -111,7 +122,7 @@ BlockBackend *blk_new_with_bs(Error **errp) } bs = bdrv_new_root(); - blk->bs = bs; + blk->root = bdrv_root_attach_child(bs, "root", &child_root); bs->blk = blk; return blk; } @@ -140,7 +151,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, return NULL; } - ret = bdrv_open(&blk->bs, filename, reference, options, flags, errp); + ret = bdrv_open(&blk->root->bs, filename, reference, options, flags, errp); if (ret < 0) { blk_unref(blk); return NULL; @@ -154,7 +165,7 @@ static void blk_delete(BlockBackend *blk) assert(!blk->refcnt); assert(!blk->name); assert(!blk->dev); - if (blk->bs) { + if (blk->root) { blk_remove_bs(blk); } assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); @@ -226,7 +237,7 @@ void blk_remove_all_bs(void) AioContext *ctx = blk_get_aio_context(blk); aio_context_acquire(ctx); - if (blk->bs) { + if (blk->root) { blk_remove_bs(blk); } aio_context_release(ctx); @@ -268,9 +279,9 @@ BlockDriverState *blk_next_root_bs(BlockDriverState *bs) do { blk = blk_all_next(blk); - } while (blk && !blk->bs); + } while (blk && !blk->root); - return blk ? blk->bs : NULL; + return blk ? blk->root->bs : NULL; } /* @@ -354,7 +365,7 @@ BlockBackend *blk_by_name(const char *name) */ BlockDriverState *blk_bs(BlockBackend *blk) { - return blk->bs; + return blk->root ? blk->root->bs : NULL; } /* @@ -364,13 +375,13 @@ void blk_set_bs(BlockBackend *blk, BlockDriverState *bs) { bdrv_ref(bs); - if (blk->bs) { - blk->bs->blk = NULL; - bdrv_unref(blk->bs); + if (blk->root) { + blk->root->bs->blk = NULL; + bdrv_root_unref_child(blk->root); } assert(bs->blk == NULL); - blk->bs = bs; + blk->root = bdrv_root_attach_child(bs, "root", &child_root); bs->blk = blk; } @@ -414,15 +425,15 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) */ void blk_remove_bs(BlockBackend *blk) { - assert(blk->bs->blk == blk); + assert(blk->root->bs->blk == blk); notifier_list_notify(&blk->remove_bs_notifiers, blk); blk_update_root_state(blk); - blk->bs->blk = NULL; - bdrv_unref(blk->bs); - blk->bs = NULL; + blk->root->bs->blk = NULL; + bdrv_root_unref_child(blk->root); + blk->root = NULL; } /* @@ -430,9 +441,9 @@ void blk_remove_bs(BlockBackend *blk) */ void blk_insert_bs(BlockBackend *blk, BlockDriverState *bs) { - assert(!blk->bs && !bs->blk); + assert(!blk->root && !bs->blk); bdrv_ref(bs); - blk->bs = bs; + blk->root = bdrv_root_attach_child(bs, "root", &child_root); bs->blk = blk; notifier_list_notify(&blk->insert_bs_notifiers, blk); @@ -614,9 +625,10 @@ void blk_iostatus_disable(BlockBackend *blk) void blk_iostatus_reset(BlockBackend *blk) { if (blk_iostatus_is_enabled(blk)) { + BlockDriverState *bs = blk_bs(blk); blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; - if (blk->bs && blk->bs->job) { - block_job_iostatus_reset(blk->bs->job); + if (bs && bs->job) { + block_job_iostatus_reset(bs->job); } } } @@ -689,7 +701,7 @@ int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, return ret; } - return bdrv_read(blk->bs, sector_num, buf, nb_sectors); + return bdrv_read(blk_bs(blk), sector_num, buf, nb_sectors); } int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, @@ -700,7 +712,7 @@ int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, return ret; } - return bdrv_read_unthrottled(blk->bs, sector_num, buf, nb_sectors); + return bdrv_read_unthrottled(blk_bs(blk), sector_num, buf, nb_sectors); } int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, @@ -711,7 +723,7 @@ int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, return ret; } - return bdrv_write(blk->bs, sector_num, buf, nb_sectors); + return bdrv_write(blk_bs(blk), sector_num, buf, nb_sectors); } int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, @@ -722,7 +734,7 @@ int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, return ret; } - return bdrv_write_zeroes(blk->bs, sector_num, nb_sectors, flags); + return bdrv_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags); } static void error_callback_bh(void *opaque) @@ -760,7 +772,7 @@ BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, return blk_abort_aio_request(blk, cb, opaque, ret); } - return bdrv_aio_write_zeroes(blk->bs, sector_num, nb_sectors, flags, + return bdrv_aio_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags, cb, opaque); } @@ -771,7 +783,7 @@ int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) return ret; } - return bdrv_pread(blk->bs, offset, buf, count); + return bdrv_pread(blk_bs(blk), offset, buf, count); } int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) @@ -781,7 +793,7 @@ int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) return ret; } - return bdrv_pwrite(blk->bs, offset, buf, count); + return bdrv_pwrite(blk_bs(blk), offset, buf, count); } int64_t blk_getlength(BlockBackend *blk) @@ -790,15 +802,15 @@ int64_t blk_getlength(BlockBackend *blk) return -ENOMEDIUM; } - return bdrv_getlength(blk->bs); + return bdrv_getlength(blk_bs(blk)); } void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr) { - if (!blk->bs) { + if (!blk_bs(blk)) { *nb_sectors_ptr = 0; } else { - bdrv_get_geometry(blk->bs, nb_sectors_ptr); + bdrv_get_geometry(blk_bs(blk), nb_sectors_ptr); } } @@ -808,7 +820,7 @@ int64_t blk_nb_sectors(BlockBackend *blk) return -ENOMEDIUM; } - return bdrv_nb_sectors(blk->bs); + return bdrv_nb_sectors(blk_bs(blk)); } BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, @@ -820,7 +832,7 @@ BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, return blk_abort_aio_request(blk, cb, opaque, ret); } - return bdrv_aio_readv(blk->bs, sector_num, iov, nb_sectors, cb, opaque); + return bdrv_aio_readv(blk_bs(blk), sector_num, iov, nb_sectors, cb, opaque); } BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, @@ -832,7 +844,7 @@ BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, return blk_abort_aio_request(blk, cb, opaque, ret); } - return bdrv_aio_writev(blk->bs, sector_num, iov, nb_sectors, cb, opaque); + return bdrv_aio_writev(blk_bs(blk), sector_num, iov, nb_sectors, cb, opaque); } BlockAIOCB *blk_aio_flush(BlockBackend *blk, @@ -842,7 +854,7 @@ BlockAIOCB *blk_aio_flush(BlockBackend *blk, return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); } - return bdrv_aio_flush(blk->bs, cb, opaque); + return bdrv_aio_flush(blk_bs(blk), cb, opaque); } BlockAIOCB *blk_aio_discard(BlockBackend *blk, @@ -854,7 +866,7 @@ BlockAIOCB *blk_aio_discard(BlockBackend *blk, return blk_abort_aio_request(blk, cb, opaque, ret); } - return bdrv_aio_discard(blk->bs, sector_num, nb_sectors, cb, opaque); + return bdrv_aio_discard(blk_bs(blk), sector_num, nb_sectors, cb, opaque); } void blk_aio_cancel(BlockAIOCB *acb) @@ -878,7 +890,7 @@ int blk_aio_multiwrite(BlockBackend *blk, BlockRequest *reqs, int num_reqs) } } - return bdrv_aio_multiwrite(blk->bs, reqs, num_reqs); + return bdrv_aio_multiwrite(blk_bs(blk), reqs, num_reqs); } int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) @@ -887,7 +899,7 @@ int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) return -ENOMEDIUM; } - return bdrv_ioctl(blk->bs, req, buf); + return bdrv_ioctl(blk_bs(blk), req, buf); } BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, @@ -897,7 +909,7 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); } - return bdrv_aio_ioctl(blk->bs, req, buf, cb, opaque); + return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque); } int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) @@ -907,7 +919,7 @@ int blk_co_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) return ret; } - return bdrv_co_discard(blk->bs, sector_num, nb_sectors); + return bdrv_co_discard(blk_bs(blk), sector_num, nb_sectors); } int blk_co_flush(BlockBackend *blk) @@ -916,7 +928,7 @@ int blk_co_flush(BlockBackend *blk) return -ENOMEDIUM; } - return bdrv_co_flush(blk->bs); + return bdrv_co_flush(blk_bs(blk)); } int blk_flush(BlockBackend *blk) @@ -925,13 +937,13 @@ int blk_flush(BlockBackend *blk) return -ENOMEDIUM; } - return bdrv_flush(blk->bs); + return bdrv_flush(blk_bs(blk)); } void blk_drain(BlockBackend *blk) { - if (blk->bs) { - bdrv_drain(blk->bs); + if (blk_bs(blk)) { + bdrv_drain(blk_bs(blk)); } } @@ -1019,8 +1031,10 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, int blk_is_read_only(BlockBackend *blk) { - if (blk->bs) { - return bdrv_is_read_only(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_is_read_only(bs); } else { return blk->root_state.read_only; } @@ -1028,17 +1042,21 @@ int blk_is_read_only(BlockBackend *blk) int blk_is_sg(BlockBackend *blk) { - if (!blk->bs) { + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { return 0; } - return bdrv_is_sg(blk->bs); + return bdrv_is_sg(bs); } int blk_enable_write_cache(BlockBackend *blk) { - if (blk->bs) { - return bdrv_enable_write_cache(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_enable_write_cache(bs); } else { return !!(blk->root_state.open_flags & BDRV_O_CACHE_WB); } @@ -1046,8 +1064,10 @@ int blk_enable_write_cache(BlockBackend *blk) void blk_set_enable_write_cache(BlockBackend *blk, bool wce) { - if (blk->bs) { - bdrv_set_enable_write_cache(blk->bs, wce); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_set_enable_write_cache(bs, wce); } else { if (wce) { blk->root_state.open_flags |= BDRV_O_CACHE_WB; @@ -1059,17 +1079,21 @@ void blk_set_enable_write_cache(BlockBackend *blk, bool wce) void blk_invalidate_cache(BlockBackend *blk, Error **errp) { - if (!blk->bs) { + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { error_setg(errp, "Device '%s' has no medium", blk->name); return; } - bdrv_invalidate_cache(blk->bs, errp); + bdrv_invalidate_cache(bs, errp); } bool blk_is_inserted(BlockBackend *blk) { - return blk->bs && bdrv_is_inserted(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + return bs && bdrv_is_inserted(bs); } bool blk_is_available(BlockBackend *blk) @@ -1079,22 +1103,28 @@ bool blk_is_available(BlockBackend *blk) void blk_lock_medium(BlockBackend *blk, bool locked) { - if (blk->bs) { - bdrv_lock_medium(blk->bs, locked); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_lock_medium(bs, locked); } } void blk_eject(BlockBackend *blk, bool eject_flag) { - if (blk->bs) { - bdrv_eject(blk->bs, eject_flag); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_eject(bs, eject_flag); } } int blk_get_flags(BlockBackend *blk) { - if (blk->bs) { - return bdrv_get_flags(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_get_flags(bs); } else { return blk->root_state.open_flags; } @@ -1102,8 +1132,10 @@ int blk_get_flags(BlockBackend *blk) int blk_get_max_transfer_length(BlockBackend *blk) { - if (blk->bs) { - return blk->bs->bl.max_transfer_length; + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bs->bl.max_transfer_length; } else { return 0; } @@ -1111,7 +1143,7 @@ int blk_get_max_transfer_length(BlockBackend *blk) int blk_get_max_iov(BlockBackend *blk) { - return blk->bs->bl.max_iov; + return blk->root->bs->bl.max_iov; } void blk_set_guest_block_size(BlockBackend *blk, int align) @@ -1121,48 +1153,58 @@ void blk_set_guest_block_size(BlockBackend *blk, int align) void *blk_try_blockalign(BlockBackend *blk, size_t size) { - return qemu_try_blockalign(blk ? blk->bs : NULL, size); + return qemu_try_blockalign(blk ? blk_bs(blk) : NULL, size); } void *blk_blockalign(BlockBackend *blk, size_t size) { - return qemu_blockalign(blk ? blk->bs : NULL, size); + return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); } bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) { - if (!blk->bs) { + BlockDriverState *bs = blk_bs(blk); + + if (!bs) { return false; } - return bdrv_op_is_blocked(blk->bs, op, errp); + return bdrv_op_is_blocked(bs, op, errp); } void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) { - if (blk->bs) { - bdrv_op_unblock(blk->bs, op, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_unblock(bs, op, reason); } } void blk_op_block_all(BlockBackend *blk, Error *reason) { - if (blk->bs) { - bdrv_op_block_all(blk->bs, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_block_all(bs, reason); } } void blk_op_unblock_all(BlockBackend *blk, Error *reason) { - if (blk->bs) { - bdrv_op_unblock_all(blk->bs, reason); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_op_unblock_all(bs, reason); } } AioContext *blk_get_aio_context(BlockBackend *blk) { - if (blk->bs) { - return bdrv_get_aio_context(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + return bdrv_get_aio_context(bs); } else { return qemu_get_aio_context(); } @@ -1176,8 +1218,10 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) void blk_set_aio_context(BlockBackend *blk, AioContext *new_context) { - if (blk->bs) { - bdrv_set_aio_context(blk->bs, new_context); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_set_aio_context(bs, new_context); } } @@ -1185,8 +1229,10 @@ void blk_add_aio_context_notifier(BlockBackend *blk, void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*detach_aio_context)(void *opaque), void *opaque) { - if (blk->bs) { - bdrv_add_aio_context_notifier(blk->bs, attached_aio_context, + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_add_aio_context_notifier(bs, attached_aio_context, detach_aio_context, opaque); } } @@ -1197,8 +1243,10 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void (*detach_aio_context)(void *), void *opaque) { - if (blk->bs) { - bdrv_remove_aio_context_notifier(blk->bs, attached_aio_context, + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_remove_aio_context_notifier(bs, attached_aio_context, detach_aio_context, opaque); } } @@ -1215,15 +1263,19 @@ void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) void blk_io_plug(BlockBackend *blk) { - if (blk->bs) { - bdrv_io_plug(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_io_plug(bs); } } void blk_io_unplug(BlockBackend *blk) { - if (blk->bs) { - bdrv_io_unplug(blk->bs); + BlockDriverState *bs = blk_bs(blk); + + if (bs) { + bdrv_io_unplug(bs); } } @@ -1246,7 +1298,7 @@ int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, return ret; } - return bdrv_co_write_zeroes(blk->bs, sector_num, nb_sectors, flags); + return bdrv_co_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags); } int blk_write_compressed(BlockBackend *blk, int64_t sector_num, @@ -1257,7 +1309,7 @@ int blk_write_compressed(BlockBackend *blk, int64_t sector_num, return ret; } - return bdrv_write_compressed(blk->bs, sector_num, buf, nb_sectors); + return bdrv_write_compressed(blk_bs(blk), sector_num, buf, nb_sectors); } int blk_truncate(BlockBackend *blk, int64_t offset) @@ -1266,7 +1318,7 @@ int blk_truncate(BlockBackend *blk, int64_t offset) return -ENOMEDIUM; } - return bdrv_truncate(blk->bs, offset); + return bdrv_truncate(blk_bs(blk), offset); } int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) @@ -1276,7 +1328,7 @@ int blk_discard(BlockBackend *blk, int64_t sector_num, int nb_sectors) return ret; } - return bdrv_discard(blk->bs, sector_num, nb_sectors); + return bdrv_discard(blk_bs(blk), sector_num, nb_sectors); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, @@ -1286,7 +1338,7 @@ int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, return -ENOMEDIUM; } - return bdrv_save_vmstate(blk->bs, buf, pos, size); + return bdrv_save_vmstate(blk_bs(blk), buf, pos, size); } int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) @@ -1295,7 +1347,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) return -ENOMEDIUM; } - return bdrv_load_vmstate(blk->bs, buf, pos, size); + return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); } int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) @@ -1304,7 +1356,7 @@ int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) return -ENOMEDIUM; } - return bdrv_probe_blocksizes(blk->bs, bsz); + return bdrv_probe_blocksizes(blk_bs(blk), bsz); } int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) @@ -1313,7 +1365,7 @@ int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) return -ENOMEDIUM; } - return bdrv_probe_geometry(blk->bs, geo); + return bdrv_probe_geometry(blk_bs(blk), geo); } /* @@ -1322,18 +1374,18 @@ int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) */ void blk_update_root_state(BlockBackend *blk) { - assert(blk->bs); + assert(blk->root); - blk->root_state.open_flags = blk->bs->open_flags; - blk->root_state.read_only = blk->bs->read_only; - blk->root_state.detect_zeroes = blk->bs->detect_zeroes; + blk->root_state.open_flags = blk->root->bs->open_flags; + blk->root_state.read_only = blk->root->bs->read_only; + blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; if (blk->root_state.throttle_group) { g_free(blk->root_state.throttle_group); throttle_group_unref(blk->root_state.throttle_state); } - if (blk->bs->throttle_state) { - const char *name = throttle_group_get_name(blk->bs); + if (blk->root->bs->throttle_state) { + const char *name = throttle_group_get_name(blk->root->bs); blk->root_state.throttle_group = g_strdup(name); blk->root_state.throttle_state = throttle_group_incref(name); } else { @@ -1382,8 +1434,8 @@ int blk_commit_all(void) AioContext *aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - if (blk_is_inserted(blk) && blk->bs->backing) { - int ret = bdrv_commit(blk->bs); + if (blk_is_inserted(blk) && blk->root->bs->backing) { + int ret = bdrv_commit(blk->root->bs); if (ret < 0) { aio_context_release(aio_context); return ret; diff --git a/include/block/block_int.h b/include/block/block_int.h index bbf617a031..c031db40d9 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -692,6 +692,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, void hmp_drive_add_node(Monitor *mon, const char *optstr); +BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, + const char *child_name, + const BdrvChildRole *child_role); +void bdrv_root_unref_child(BdrvChild *child); + void blk_set_bs(BlockBackend *blk, BlockDriverState *bs); void blk_dev_change_media_cb(BlockBackend *blk, bool load); From 1bf1cbc91f3575ea27e33d0cd2cc49db81ffb2f6 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:47 +0100 Subject: [PATCH 21/29] block: Use blk_co_preadv() for blk_read() This patch introduces blk_co_preadv() as a central function on the BlockBackend level that is supposed to handle all read requests from the BB to its root BDS eventually. Signed-off-by: Kevin Wolf --- block/block-backend.c | 64 ++++++++++++++++++++++++++++++++++++--- block/io.c | 5 +-- include/block/block_int.h | 4 +++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 5de785026b..c05878526a 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -22,6 +22,8 @@ /* Number of coroutines to reserve per attached device model */ #define COROUTINE_POOL_RESERVATION 64 +#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ + static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); struct BlockBackend { @@ -693,15 +695,69 @@ static int blk_check_request(BlockBackend *blk, int64_t sector_num, nb_sectors * BDRV_SECTOR_SIZE); } -int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors) +static int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, + unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); + int ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { return ret; } - return bdrv_read(blk_bs(blk), sector_num, buf, nb_sectors); + return bdrv_co_do_preadv(blk_bs(blk), offset, bytes, qiov, flags); +} + +typedef struct BlkRwCo { + BlockBackend *blk; + int64_t offset; + QEMUIOVector *qiov; + int ret; + BdrvRequestFlags flags; +} BlkRwCo; + +static void blk_read_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + + rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size, + rwco->qiov, rwco->flags); +} + +int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + AioContext *aio_context; + QEMUIOVector qiov; + struct iovec iov; + Coroutine *co; + BlkRwCo rwco; + + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + iov = (struct iovec) { + .iov_base = buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + rwco = (BlkRwCo) { + .blk = blk, + .offset = sector_num << BDRV_SECTOR_BITS, + .qiov = &qiov, + .ret = NOT_DONE, + }; + + co = qemu_coroutine_create(blk_read_entry); + qemu_coroutine_enter(co, &rwco); + + aio_context = blk_get_aio_context(blk); + while (rwco.ret == NOT_DONE) { + aio_poll(aio_context, true); + } + + return rwco.ret; } int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, diff --git a/block/io.c b/block/io.c index 5f9b6d623d..c7084e4932 100644 --- a/block/io.c +++ b/block/io.c @@ -44,9 +44,6 @@ static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov); -static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); @@ -939,7 +936,7 @@ out: /* * Handle a read request in coroutine context */ -static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, +int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { diff --git a/include/block/block_int.h b/include/block/block_int.h index c031db40d9..b78be8daf8 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -508,6 +508,10 @@ extern BlockDriver bdrv_qcow2; */ void bdrv_setup_io_funcs(BlockDriver *bdrv); +int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); + int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, const char *filename); From a8823a3bfd8a8adfa11dea61b151175a02caa7d0 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:48 +0100 Subject: [PATCH 22/29] block: Use blk_co_pwritev() for blk_write() Signed-off-by: Kevin Wolf --- block/block-backend.c | 39 ++++++++++++++++++++++++++++++--------- block/io.c | 5 +---- include/block/block_int.h | 3 +++ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index c05878526a..5b15475c13 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -707,6 +707,18 @@ static int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, return bdrv_co_do_preadv(blk_bs(blk), offset, bytes, qiov, flags); } +static int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, + unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + int ret = blk_check_byte_request(blk, offset, bytes); + if (ret < 0) { + return ret; + } + + return bdrv_co_do_pwritev(blk_bs(blk), offset, bytes, qiov, flags); +} + typedef struct BlkRwCo { BlockBackend *blk; int64_t offset; @@ -723,8 +735,16 @@ static void blk_read_entry(void *opaque) rwco->qiov, rwco->flags); } -int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors) +static void blk_write_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + + rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, rwco->qiov->size, + rwco->qiov, rwco->flags); +} + +static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors, CoroutineEntry co_entry) { AioContext *aio_context; QEMUIOVector qiov; @@ -749,7 +769,7 @@ int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, .ret = NOT_DONE, }; - co = qemu_coroutine_create(blk_read_entry); + co = qemu_coroutine_create(co_entry); qemu_coroutine_enter(co, &rwco); aio_context = blk_get_aio_context(blk); @@ -760,6 +780,12 @@ int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, return rwco.ret; } +int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors) +{ + return blk_rw(blk, sector_num, buf, nb_sectors, blk_read_entry); +} + int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { @@ -774,12 +800,7 @@ int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return ret; - } - - return bdrv_write(blk_bs(blk), sector_num, buf, nb_sectors); + return blk_rw(blk, sector_num, (uint8_t*) buf, nb_sectors, blk_write_entry); } int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, diff --git a/block/io.c b/block/io.c index c7084e4932..aa8537ce2e 100644 --- a/block/io.c +++ b/block/io.c @@ -44,9 +44,6 @@ static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs, static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov); -static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags); static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, @@ -1281,7 +1278,7 @@ fail: /* * Handle a write request in coroutine context */ -static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, +int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { diff --git a/include/block/block_int.h b/include/block/block_int.h index b78be8daf8..ba6e9ac696 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -511,6 +511,9 @@ void bdrv_setup_io_funcs(BlockDriver *bdrv); int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); +int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, + int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags); int get_tmp_filename(char *filename, int size); BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, From 5bd51196676b07d3f9ddb3086057b0d82ae95f36 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:49 +0100 Subject: [PATCH 23/29] block: Pull up blk_read_unthrottled() implementation Use blk_read(), so that it goes through blk_co_preadv() like all read requests from the BB to the BDS. Signed-off-by: Kevin Wolf --- block/block-backend.c | 12 ++++++++++-- block/io.c | 14 -------------- include/block/block.h | 2 -- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 5b15475c13..475d27a4da 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -789,12 +789,20 @@ int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { - int ret = blk_check_request(blk, sector_num, nb_sectors); + BlockDriverState *bs = blk_bs(blk); + bool enabled; + int ret; + + ret = blk_check_request(blk, sector_num, nb_sectors); if (ret < 0) { return ret; } - return bdrv_read_unthrottled(blk_bs(blk), sector_num, buf, nb_sectors); + enabled = bs->io_limits_enabled; + bs->io_limits_enabled = false; + ret = blk_read(blk, sector_num, buf, nb_sectors); + bs->io_limits_enabled = enabled; + return ret; } int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, diff --git a/block/io.c b/block/io.c index aa8537ce2e..41d954cad2 100644 --- a/block/io.c +++ b/block/io.c @@ -615,20 +615,6 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false, 0); } -/* Just like bdrv_read(), but with I/O throttling temporarily disabled */ -int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) -{ - bool enabled; - int ret; - - enabled = bs->io_limits_enabled; - bs->io_limits_enabled = false; - ret = bdrv_read(bs, sector_num, buf, nb_sectors); - bs->io_limits_enabled = enabled; - return ret; -} - /* Return < 0 if error. Important errors are: -EIO generic I/O error (may happen for all errors) -ENOMEDIUM No media inserted. diff --git a/include/block/block.h b/include/block/block.h index ea8ed04403..01349efad5 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -229,8 +229,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state); int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); -int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors); int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num, From fc1453cdfc815e879829ec1ebbc8a0667f86959c Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:50 +0100 Subject: [PATCH 24/29] block: Use blk_co_pwritev() in blk_write_zeroes() Signed-off-by: Kevin Wolf --- block/block-backend.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 475d27a4da..886c2f4023 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -744,7 +744,8 @@ static void blk_write_entry(void *opaque) } static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors, CoroutineEntry co_entry) + int nb_sectors, CoroutineEntry co_entry, + BdrvRequestFlags flags) { AioContext *aio_context; QEMUIOVector qiov; @@ -766,6 +767,7 @@ static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, .blk = blk, .offset = sector_num << BDRV_SECTOR_BITS, .qiov = &qiov, + .flags = flags, .ret = NOT_DONE, }; @@ -783,7 +785,7 @@ static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { - return blk_rw(blk, sector_num, buf, nb_sectors, blk_read_entry); + return blk_rw(blk, sector_num, buf, nb_sectors, blk_read_entry, 0); } int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, @@ -808,18 +810,15 @@ int blk_read_unthrottled(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int blk_write(BlockBackend *blk, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - return blk_rw(blk, sector_num, (uint8_t*) buf, nb_sectors, blk_write_entry); + return blk_rw(blk, sector_num, (uint8_t*) buf, nb_sectors, + blk_write_entry, 0); } int blk_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return ret; - } - - return bdrv_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags); + return blk_rw(blk, sector_num, NULL, nb_sectors, blk_write_entry, + BDRV_REQ_ZERO_WRITE); } static void error_callback_bh(void *opaque) From a55d3fba99cd7608349abf4ea048783439956c1d Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:51 +0100 Subject: [PATCH 25/29] block: Use blk_prw() in blk_pread()/blk_pwrite() Signed-off-by: Kevin Wolf --- block/block-backend.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 886c2f4023..6048b07657 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -743,9 +743,9 @@ static void blk_write_entry(void *opaque) rwco->qiov, rwco->flags); } -static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, - int nb_sectors, CoroutineEntry co_entry, - BdrvRequestFlags flags) +static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, + int64_t bytes, CoroutineEntry co_entry, + BdrvRequestFlags flags) { AioContext *aio_context; QEMUIOVector qiov; @@ -753,19 +753,15 @@ static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, Coroutine *co; BlkRwCo rwco; - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - iov = (struct iovec) { .iov_base = buf, - .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + .iov_len = bytes, }; qemu_iovec_init_external(&qiov, &iov, 1); rwco = (BlkRwCo) { .blk = blk, - .offset = sector_num << BDRV_SECTOR_BITS, + .offset = offset, .qiov = &qiov, .flags = flags, .ret = NOT_DONE, @@ -782,6 +778,18 @@ static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, return rwco.ret; } +static int blk_rw(BlockBackend *blk, int64_t sector_num, uint8_t *buf, + int nb_sectors, CoroutineEntry co_entry, + BdrvRequestFlags flags) +{ + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; + } + + return blk_prw(blk, sector_num << BDRV_SECTOR_BITS, buf, + nb_sectors << BDRV_SECTOR_BITS, co_entry, flags); +} + int blk_read(BlockBackend *blk, int64_t sector_num, uint8_t *buf, int nb_sectors) { @@ -862,22 +870,20 @@ BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) { - int ret = blk_check_byte_request(blk, offset, count); + int ret = blk_prw(blk, offset, buf, count, blk_read_entry, 0); if (ret < 0) { return ret; } - - return bdrv_pread(blk_bs(blk), offset, buf, count); + return count; } int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count) { - int ret = blk_check_byte_request(blk, offset, count); + int ret = blk_prw(blk, offset, (void*) buf, count, blk_write_entry, 0); if (ret < 0) { return ret; } - - return bdrv_pwrite(blk_bs(blk), offset, buf, count); + return count; } int64_t blk_getlength(BlockBackend *blk) From 57d6a42883b528e0078bb095398144740e1a4014 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:52 +0100 Subject: [PATCH 26/29] block: Use blk_aio_prwv() for aio_read/write/write_zeroes Signed-off-by: Kevin Wolf --- block/block-backend.c | 105 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 6048b07657..972a6177c9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -855,17 +855,96 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, return &acb->common; } +typedef struct BlkAioEmAIOCB { + BlockAIOCB common; + BlkRwCo rwco; + bool has_returned; + QEMUBH* bh; +} BlkAioEmAIOCB; + +static const AIOCBInfo blk_aio_em_aiocb_info = { + .aiocb_size = sizeof(BlkAioEmAIOCB), +}; + +static void blk_aio_complete(BlkAioEmAIOCB *acb) +{ + if (acb->bh) { + assert(acb->has_returned); + qemu_bh_delete(acb->bh); + } + if (acb->has_returned) { + acb->common.cb(acb->common.opaque, acb->rwco.ret); + qemu_aio_unref(acb); + } +} + +static void blk_aio_complete_bh(void *opaque) +{ + blk_aio_complete(opaque); +} + +static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, + QEMUIOVector *qiov, CoroutineEntry co_entry, + BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque) +{ + BlkAioEmAIOCB *acb; + Coroutine *co; + + acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); + acb->rwco = (BlkRwCo) { + .blk = blk, + .offset = offset, + .qiov = qiov, + .flags = flags, + .ret = NOT_DONE, + }; + acb->bh = NULL; + acb->has_returned = false; + + co = qemu_coroutine_create(co_entry); + qemu_coroutine_enter(co, acb); + + acb->has_returned = true; + if (acb->rwco.ret != NOT_DONE) { + acb->bh = aio_bh_new(blk_get_aio_context(blk), blk_aio_complete_bh, acb); + qemu_bh_schedule(acb->bh); + } + + return &acb->common; +} + +static void blk_aio_read_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size, + rwco->qiov, rwco->flags); + blk_aio_complete(acb); +} + +static void blk_aio_write_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, + rwco->qiov ? rwco->qiov->size : 0, + rwco->qiov, rwco->flags); + blk_aio_complete(acb); +} + BlockAIOCB *blk_aio_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return blk_abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags, - cb, opaque); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, NULL, + blk_aio_write_entry, BDRV_REQ_ZERO_WRITE, cb, opaque); } int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count) @@ -917,24 +996,24 @@ BlockAIOCB *blk_aio_readv(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return blk_abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_readv(blk_bs(blk), sector_num, iov, nb_sectors, cb, opaque); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, iov, + blk_aio_read_entry, 0, cb, opaque); } BlockAIOCB *blk_aio_writev(BlockBackend *blk, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return blk_abort_aio_request(blk, cb, opaque, ret); + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return blk_abort_aio_request(blk, cb, opaque, -EINVAL); } - return bdrv_aio_writev(blk_bs(blk), sector_num, iov, nb_sectors, cb, opaque); + return blk_aio_prwv(blk, sector_num << BDRV_SECTOR_BITS, iov, + blk_aio_write_entry, 0, cb, opaque); } BlockAIOCB *blk_aio_flush(BlockBackend *blk, From 8896e08814a99ad9e3f25ad1fd0b51721c3baa77 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 8 Mar 2016 13:47:53 +0100 Subject: [PATCH 27/29] block: Use blk_co_pwritev() in blk_co_write_zeroes() Signed-off-by: Kevin Wolf --- block/block-backend.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/block/block-backend.c b/block/block-backend.c index 972a6177c9..dca21d1eeb 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1462,12 +1462,13 @@ void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, int coroutine_fn blk_co_write_zeroes(BlockBackend *blk, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { - int ret = blk_check_request(blk, sector_num, nb_sectors); - if (ret < 0) { - return ret; + if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return -EINVAL; } - return bdrv_co_write_zeroes(blk_bs(blk), sector_num, nb_sectors, flags); + return blk_co_pwritev(blk, sector_num << BDRV_SECTOR_BITS, + nb_sectors << BDRV_SECTOR_BITS, NULL, + BDRV_REQ_ZERO_WRITE); } int blk_write_compressed(BlockBackend *blk, int64_t sector_num, From 6049490df4e1d89e239b7d9908b3f3213f73bdc5 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 15 Mar 2016 11:41:35 +0200 Subject: [PATCH 28/29] quorum: Emit QUORUM_REPORT_BAD for reads in fifo mode If there's an I/O error in one of Quorum children then QEMU should emit QUORUM_REPORT_BAD. However this is not working with read-pattern=fifo. This patch fixes this problem. Signed-off-by: Alberto Garcia Message-id: d57e39e8d3e8564003a1e2aadbd29c97286eb2d2.1458034554.git.berto@igalia.com Reviewed-by: Max Reitz Signed-off-by: Max Reitz --- block/quorum.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/block/quorum.c b/block/quorum.c index 3d473515a8..da15465a9a 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -284,9 +284,17 @@ static void quorum_aio_cb(void *opaque, int ret) QuorumChildRequest *sacb = opaque; QuorumAIOCB *acb = sacb->parent; BDRVQuorumState *s = acb->common.bs->opaque; - QuorumOpType type; bool rewrite = false; + if (ret == 0) { + acb->success_count++; + } else { + QuorumOpType type; + type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; + quorum_report_bad(type, acb->sector_num, acb->nb_sectors, + sacb->aiocb->bs->node_name, ret); + } + if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { /* We try to read next child in FIFO order if we fail to read */ if (ret < 0 && (acb->child_iter + 1) < s->num_children) { @@ -303,15 +311,8 @@ static void quorum_aio_cb(void *opaque, int ret) return; } - type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; sacb->ret = ret; acb->count++; - if (ret == 0) { - acb->success_count++; - } else { - quorum_report_bad(type, acb->sector_num, acb->nb_sectors, - sacb->aiocb->bs->node_name, ret); - } assert(acb->count <= s->num_children); assert(acb->success_count <= s->num_children); if (acb->count < s->num_children) { From 509565f36f1bbff2c29c0f02e7aab65cdbd9a37a Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 15 Mar 2016 11:41:36 +0200 Subject: [PATCH 29/29] iotests: Test QUORUM_REPORT_BAD in fifo mode Signed-off-by: Alberto Garcia Message-id: c0a8dbfdbe939520cda5f661af6f1cd7b6b4df9d.1458034554.git.berto@igalia.com Reviewed-by: Max Reitz Signed-off-by: Max Reitz --- tests/qemu-iotests/148 | 17 +++++++++++++++-- tests/qemu-iotests/148.out | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 index 30bc37958e..d066ec3e41 100644 --- a/tests/qemu-iotests/148 +++ b/tests/qemu-iotests/148 @@ -35,6 +35,7 @@ sector_size = 512 offset = 10 class TestQuorumEvents(iotests.QMPTestCase): + read_pattern = 'quorum' def create_blkdebug_file(self, blkdebug_file, bad_sector): file = open(blkdebug_file, 'w') @@ -48,6 +49,7 @@ sector = "%d" def setUp(self): driveopts = ['driver=quorum', 'vote-threshold=2'] + driveopts.append('read-pattern=%s' % self.read_pattern) for i in range(len(imgs)): iotests.qemu_img('create', '-f', iotests.imgfmt, imgs[i], '1M') self.create_blkdebug_file(img_conf[i], i + offset) @@ -112,7 +114,11 @@ sector = "%d" self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % ((offset + i) * sector_size, sector_size)) self.vm.qtest("clock_step %d" % delay) - self.do_check_event('img%d' % i, offset + i) + # In fifo mode only errors in the first child are detected + if i > 0 and self.read_pattern == 'fifo': + self.do_check_event(None) + else: + self.do_check_event('img%d' % i, offset + i) # I/O errors in different children: all events are emitted delay = 2 * event_rate @@ -120,10 +126,17 @@ sector = "%d" self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % ((offset + i) * sector_size, sector_size)) self.vm.qtest("clock_step %d" % delay) - self.do_check_event('img%d' % i, offset + i) + # In fifo mode only errors in the first child are detected + if i > 0 and self.read_pattern == 'fifo': + self.do_check_event(None) + else: + self.do_check_event('img%d' % i, offset + i) # No more pending events self.do_check_event(None) +class TestFifoQuorumEvents(TestQuorumEvents): + read_pattern = 'fifo' + if __name__ == '__main__': iotests.main(supported_fmts=["raw"]) diff --git a/tests/qemu-iotests/148.out b/tests/qemu-iotests/148.out index ae1213e6f8..fbc63e62f8 100644 --- a/tests/qemu-iotests/148.out +++ b/tests/qemu-iotests/148.out @@ -1,5 +1,5 @@ -. +.. ---------------------------------------------------------------------- -Ran 1 tests +Ran 2 tests OK