From 65f33bc0020112e7be7b8966495cd5efa2d0ab15 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sun, 4 May 2014 05:31:40 +0200 Subject: qcow2: Fix alloc_clusters_noref() overflow detection If the very first allocation has a length of 0, the free_cluster_index is still 0 after the for loop, which means that subtracting one from it will underflow and signal an invalid range of clusters by returning -EFBIG. However, there is no such range, as its length is 0. Fix this by preventing underflows on free_cluster_index during the check. Signed-off-by: Max Reitz Reviewed-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- block/qcow2-refcount.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index e79895d..9507aef 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -656,7 +656,9 @@ retry: /* Make sure that all offsets in the "allocated" range are representable * in an int64_t */ - if (s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) { + if (s->free_cluster_index > 0 && + s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) + { return -EFBIG; } -- cgit v1.1 From f915db07ef9c368ea6db6430256de064fdd1525f Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Sat, 3 May 2014 16:47:08 +0200 Subject: iotests: Use configured python Currently, QEMU's iotests rely on /usr/bin/env to start the correct Python (that is, at least Python 2.4, but not 3). On systems where Python 3 is the default, the user has no clean way of making the iotests use the correct binary. This commit makes the iotests use the Python selected by configure. Signed-off-by: Max Reitz Signed-off-by: Stefan Hajnoczi --- configure | 6 ++++++ tests/qemu-iotests/031 | 9 +++++---- tests/qemu-iotests/036 | 7 ++++--- tests/qemu-iotests/039 | 19 ++++++++++--------- tests/qemu-iotests/054 | 3 ++- tests/qemu-iotests/060 | 21 +++++++++++---------- tests/qemu-iotests/061 | 25 +++++++++++++------------ tests/qemu-iotests/065 | 2 +- tests/qemu-iotests/083 | 3 ++- tests/qemu-iotests/check | 18 ++++++++++++++++-- 10 files changed, 70 insertions(+), 43 deletions(-) diff --git a/configure b/configure index 46bc0c9..3c11dd0 100755 --- a/configure +++ b/configure @@ -4771,6 +4771,12 @@ if test "$gcov" = "yes" ; then echo "GCOV=$gcov_tool" >> $config_host_mak fi +iotests_common_env="tests/qemu-iotests/common.env" + +echo "# Automatically generated by configure - do not modify" > $iotests_common_env +echo >> $iotests_common_env +echo "PYTHON='$python'" >> $iotests_common_env + # use included Linux headers if test "$linux" = "yes" ; then mkdir -p linux-headers diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 1d920ea..5aefb88 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -35,6 +35,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter . ./common.pattern @@ -56,22 +57,22 @@ for IMGOPTS in "compat=0.10" "compat=1.1"; do echo === Create image with unknown header extension === echo _make_test_img 64M - ./qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension" - ./qcow2.py "$TEST_IMG" dump-header + $PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension" + $PYTHON qcow2.py "$TEST_IMG" dump-header _check_test_img echo echo === Rewrite header with no backing file === echo $QEMU_IMG rebase -u -b "" "$TEST_IMG" - ./qcow2.py "$TEST_IMG" dump-header + $PYTHON qcow2.py "$TEST_IMG" dump-header _check_test_img echo echo === Add a backing file and format === echo $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG" - ./qcow2.py "$TEST_IMG" dump-header + $PYTHON qcow2.py "$TEST_IMG" dump-header done # success, all done diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index 03b6aa9..29c35d1 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -38,6 +38,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter . ./common.pattern @@ -53,15 +54,15 @@ IMGOPTS="compat=1.1" echo === Create image with unknown autoclear feature bit === echo _make_test_img 64M -./qcow2.py "$TEST_IMG" set-feature-bit autoclear 63 -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63 +$PYTHON qcow2.py "$TEST_IMG" dump-header echo echo === Repair image === echo _check_test_img -r all -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header # success, all done echo "*** done" diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index b9cbe99..b7b7030 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -38,6 +38,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter @@ -58,7 +59,7 @@ _make_test_img $size $QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img echo @@ -73,7 +74,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img echo @@ -82,7 +83,7 @@ echo "== Read-only access must still work ==" $QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Repairing the image file must succeed ==" @@ -90,7 +91,7 @@ echo "== Repairing the image file must succeed ==" _check_test_img -r all # The dirty bit must not be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Data should still be accessible after repair ==" @@ -109,12 +110,12 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Creating an image file with lazy_refcounts=off ==" @@ -128,7 +129,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io ulimit -c "$old_ulimit" # The dirty bit must not be set since lazy_refcounts=off -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img echo @@ -144,8 +145,8 @@ $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG commit "$TEST_IMG" # The dirty bit must not be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features -./qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features _check_test_img TEST_IMG="$TEST_IMG".base _check_test_img diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 index c8b7082..a5ebf99 100755 --- a/tests/qemu-iotests/054 +++ b/tests/qemu-iotests/054 @@ -35,6 +35,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter @@ -49,7 +50,7 @@ _make_test_img $((1024*1024))T echo echo "creating too large image (1 EB) using qcow2.py" _make_test_img 4G -./qcow2.py "$TEST_IMG" set-header size $((1024 ** 6)) +$PYTHON qcow2.py "$TEST_IMG" set-header size $((1024 ** 6)) _check_test_img # success, all done diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index f0116aa..5447b27 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -35,6 +35,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter @@ -68,13 +69,13 @@ poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00" _check_test_img # The corrupt bit should not be set anyway -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Try to write something, thereby forcing the corrupt bit to be set $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io # The corrupt bit must now be set -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Try to open the image R/W (which should fail) $QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \ @@ -99,19 +100,19 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01" # Redirect new data cluster onto refcount block poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00" _check_test_img -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Try to fix it _check_test_img -r all # The corrupt bit should be cleared -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Look if it's really really fixed $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "=== Testing cluster data reference into inactive L2 table ===" @@ -124,13 +125,13 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \ "\x80\x00\x00\x00\x00\x04\x00\x00" _check_test_img -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img -r all -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features # Check data $QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index d3a6b38..0de7897 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -35,6 +35,7 @@ _cleanup() trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter @@ -48,9 +49,9 @@ echo "=== Testing version downgrade with zero expansion ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -59,9 +60,9 @@ echo "=== Testing dirty version downgrade ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -69,11 +70,11 @@ echo echo "=== Testing version downgrade with unknown compat/autoclear flags ===" echo IMGOPTS="compat=1.1" _make_test_img 64M -./qcow2.py "$TEST_IMG" set-feature-bit compatible 42 -./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42 -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42 +$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42 +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header _check_test_img echo @@ -81,9 +82,9 @@ echo "=== Testing version upgrade and resize ===" echo IMGOPTS="compat=0.10" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG" -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -92,9 +93,9 @@ echo "=== Testing dirty lazy_refcounts=off ===" echo IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG" -./qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index ab5445f..e89b61d 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # # Test for additional information emitted by qemu-img info on qcow2 # images diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 index f764534..6a52c96 100755 --- a/tests/qemu-iotests/083 +++ b/tests/qemu-iotests/083 @@ -29,6 +29,7 @@ tmp=/tmp/$$ status=1 # failure is the default! # get standard environment, filters and checks +. ./common.env . ./common.rc . ./common.filter @@ -81,7 +82,7 @@ EOF nbd_url="nbd:127.0.0.1:$port:exportname=foo" fi - ./nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null & + $PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null & wait_for_tcp_port "127.0.0.1:$port" $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index e2ed5a9..ca2ee43 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -34,6 +34,13 @@ timestamp=${TIMESTAMP:=false} # generic initialization iam=check +# we need common.env +if ! . ./common.env +then + echo "$iam: failed to source common.env" + exit 1 +fi + # we need common.config if ! . ./common.config then @@ -215,9 +222,16 @@ do start=`_wallclock` $timestamp && echo -n " ["`date "+%T"`"]" - [ ! -x $seq ] && chmod u+x $seq # ensure we can run it + + if [ "$(head -n 1 $seq)" == "#!/usr/bin/env python" ]; then + run_command="$PYTHON $seq" + else + [ ! -x $seq ] && chmod u+x $seq # ensure we can run it + run_command="./$seq" + fi + MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \ - ./$seq >$tmp.out 2>&1 + $run_command >$tmp.out 2>&1 sts=$? $timestamp && _timestamp stop=`_wallclock` -- cgit v1.1 From 1a443c1b8b4314d365e82bddeb1de5b4b1c15fb3 Mon Sep 17 00:00:00 2001 From: Mike Day Date: Mon, 5 May 2014 12:53:34 -0400 Subject: qemu-img: sort block formats in help message The help message for qemu-img lists the supported block formats, of which there are 27 as of version 2.0.50. The formats are printed in the order of their driver's position in a linked list, which appears random. This patch prints the formats in sorted order, making it easier to read and to find a specific format in the list. [Added suggestions from Fam Zheng to declare variables at the top of the scope in help() and to omit explicit cast for void* opaque. --Stefan] Signed-off-by: Mike Day Signed-off-by: Stefan Hajnoczi --- qemu-img.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 96f4463..317bc6c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -32,6 +32,7 @@ #include "block/block_int.h" #include "block/qapi.h" #include +#include #define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION \ ", Copyright (c) 2004-2008 Fabrice Bellard\n" @@ -55,9 +56,25 @@ typedef enum OutputFormat { #define BDRV_O_FLAGS BDRV_O_CACHE_WB #define BDRV_DEFAULT_CACHE "writeback" -static void format_print(void *opaque, const char *name) +static gint compare_data(gconstpointer a, gconstpointer b, gpointer user) { - printf(" %s", name); + return g_strcmp0(a, b); +} + +static void print_format(gpointer data, gpointer user) +{ + printf(" %s", (char *)data); +} + +static void add_format_to_seq(void *opaque, const char *fmt_name) +{ + GSequence *seq = opaque; + + if (!g_sequence_lookup(seq, (gpointer)fmt_name, + compare_data, NULL)) { + g_sequence_insert_sorted(seq, (gpointer)fmt_name, + compare_data, NULL); + } } static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) @@ -142,10 +159,15 @@ static void QEMU_NORETURN help(void) " '-f' first image format\n" " '-F' second image format\n" " '-s' run in Strict mode - fail on different image size or sector allocation\n"; + GSequence *seq; printf("%s\nSupported formats:", help_msg); - bdrv_iterate_format(format_print, NULL); + seq = g_sequence_new(NULL); + bdrv_iterate_format(add_format_to_seq, seq); + g_sequence_foreach(seq, print_format, NULL); printf("\n"); + g_sequence_free(seq); + exit(EXIT_SUCCESS); } -- cgit v1.1 From 5f4d5e1aa652712ea39b6fd97114378c5c15ccda Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 5 May 2014 20:27:49 +0200 Subject: block/nfs: Check for NULL server part After the URL has been parsed make sure the server part is valid in order to avoid a segmentation fault when calling nfs_mount(). Signed-off-by: Max Reitz Signed-off-by: Stefan Hajnoczi --- block/nfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/nfs.c b/block/nfs.c index 9fa831f..539bd95 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -256,6 +256,10 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename, error_setg(errp, "Invalid URL specified"); goto fail; } + if (!uri->server) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } strp = strrchr(uri->path, '/'); if (strp == NULL) { error_setg(errp, "Invalid URL specified"); -- cgit v1.1 From ec209aca8340be1c81afe13fbe0ba135e5d3b9f4 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 28 Apr 2014 13:16:30 +0200 Subject: block/iscsi: bump year in copyright notice Signed-off-by: Peter Lieven Signed-off-by: Stefan Hajnoczi --- block/iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/iscsi.c b/block/iscsi.c index a30202b..52355b8 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2,7 +2,7 @@ * QEMU Block driver for iSCSI images * * Copyright (c) 2010-2011 Ronnie Sahlberg - * Copyright (c) 2012-2013 Peter Lieven + * Copyright (c) 2012-2014 Peter Lieven * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal -- cgit v1.1 From 85f49cad879adfb5c3cbdc47ca3c3b50eb8f40bc Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 6 May 2014 21:08:43 +0800 Subject: qemu-img: Convert by cluster size if target is compressed If target block driver forces compression, qemu-img convert needs to write by cluster size as well as "-c" option. Particularly, this applies for converting to VMDK streamOptimized format. Signed-off-by: Fam Zheng Signed-off-by: Stefan Hajnoczi --- include/block/block.h | 4 ++++ qemu-img.c | 1 + 2 files changed, 5 insertions(+) diff --git a/include/block/block.h b/include/block/block.h index 467fb2b..27d8598 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -34,6 +34,10 @@ typedef struct BlockDriverInfo { * opened with BDRV_O_UNMAP flag for this to work. */ bool can_write_zeroes_with_unmap; + /* + * True if this block driver only supports compressed writes + */ + bool needs_compressed_writes; } BlockDriverInfo; typedef struct BlockFragInfo { diff --git a/qemu-img.c b/qemu-img.c index 317bc6c..04ce02a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1502,6 +1502,7 @@ static int img_convert(int argc, char **argv) goto out; } } else { + compress = compress || bdi.needs_compressed_writes; cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; } -- cgit v1.1 From ba0ad89e2c3316ce26ca6576a3c5051f91bfc0fe Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 6 May 2014 21:08:44 +0800 Subject: vmdk: Implement .bdrv_write_compressed Add a wrapper function to support "compressed" path in qemu-img convert. Only support streamOptimized subformat case for now (num_extents == 1 and extent compression is true). Signed-off-by: Fam Zheng Signed-off-by: Stefan Hajnoczi --- block/vmdk.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/block/vmdk.c b/block/vmdk.c index 06a1f9f..057c3f1 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1496,6 +1496,19 @@ static coroutine_fn int vmdk_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } +static int vmdk_write_compressed(BlockDriverState *bs, + int64_t sector_num, + const uint8_t *buf, + int nb_sectors) +{ + BDRVVmdkState *s = bs->opaque; + if (s->num_extents == 1 && s->extents[0].compressed) { + return vmdk_write(bs, sector_num, buf, nb_sectors, false, false); + } else { + return -ENOTSUP; + } +} + static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, @@ -2109,6 +2122,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_reopen_prepare = vmdk_reopen_prepare, .bdrv_read = vmdk_co_read, .bdrv_write = vmdk_co_write, + .bdrv_write_compressed = vmdk_write_compressed, .bdrv_co_write_zeroes = vmdk_co_write_zeroes, .bdrv_close = vmdk_close, .bdrv_create = vmdk_create, -- cgit v1.1 From 74fe188cd1a27a5565787152d8620f8b8d04c4f9 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 6 May 2014 21:08:45 +0800 Subject: vmdk: Implement .bdrv_get_info() This will return cluster_size and needs_compressed_writes to caller, if all the extents have the same value (or there's only one extent). Otherwise return -ENOTSUP. cluster_size is only reported for sparse formats. Signed-off-by: Fam Zheng Signed-off-by: Stefan Hajnoczi --- block/vmdk.c | 21 +++++++++++++++++++++ tests/qemu-iotests/059.out | 1 + 2 files changed, 22 insertions(+) diff --git a/block/vmdk.c b/block/vmdk.c index 057c3f1..480ea37 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2076,6 +2076,26 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) return spec_info; } +static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + int i; + BDRVVmdkState *s = bs->opaque; + assert(s->num_extents); + bdi->needs_compressed_writes = s->extents[0].compressed; + if (!s->extents[0].flat) { + bdi->cluster_size = s->extents[0].cluster_sectors << BDRV_SECTOR_BITS; + } + /* See if we have multiple extents but they have different cases */ + for (i = 1; i < s->num_extents; i++) { + if (bdi->needs_compressed_writes != s->extents[i].compressed || + (bdi->cluster_size && bdi->cluster_size != + s->extents[i].cluster_sectors << BDRV_SECTOR_BITS)) { + return -ENOTSUP; + } + } + return 0; +} + static QEMUOptionParameter vmdk_create_options[] = { { .name = BLOCK_OPT_SIZE, @@ -2132,6 +2152,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_has_zero_init = vmdk_has_zero_init, .bdrv_get_specific_info = vmdk_get_specific_info, .bdrv_refresh_limits = vmdk_refresh_limits, + .bdrv_get_info = vmdk_get_info, .create_options = vmdk_create_options, }; diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 3371c86..14c0957 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2050,6 +2050,7 @@ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least image: TEST_DIR/iotest-version3.IMGFMT file format: IMGFMT virtual size: 1.0G (1073741824 bytes) +cluster_size: 65536 === Testing 4TB monolithicFlat creation and IO === Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104 -- cgit v1.1 From 10f08a0a3435afea441db8d0981dbad49042c7cf Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 6 May 2014 21:08:46 +0800 Subject: qemu-iotests: Test converting to streamOptimized from small cluster size Signed-off-by: Fam Zheng Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/059 | 7 +++++++ tests/qemu-iotests/059.out | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index ca5aa16..26a2fd3 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -104,6 +104,13 @@ truncate -s 10M $TEST_IMG _img_info echo +echo "=== Converting to streamOptimized from image with small cluster size===" +TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 IMGOPTS="cluster_size=4096" _make_test_img 1G +$QEMU_IO -c "write -P 0xa 0 512" "$TEST_IMG.qcow2" | _filter_qemu_io +$QEMU_IO -c "write -P 0xb 10240 512" "$TEST_IMG.qcow2" | _filter_qemu_io +$QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2" "$TEST_IMG" 2>&1 + +echo echo "=== Testing version 3 ===" _use_sample_img iotest-version3.vmdk.bz2 _img_info diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 14c0957..eba0ded 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -2046,6 +2046,13 @@ RW 12582912 VMFS "dummy.IMGFMT" 1 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes +=== Converting to streamOptimized from image with small cluster size=== +Formatting 'TEST_DIR/t.vmdk.IMGFMT', fmt=IMGFMT size=1073741824 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 10240 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + === Testing version 3 === image: TEST_DIR/iotest-version3.IMGFMT file format: IMGFMT -- cgit v1.1 From b1e6fc0817dca14a3581d7b0979a5885608981f2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 6 May 2014 12:11:42 +0200 Subject: block: Fix open flags with BDRV_O_SNAPSHOT The immediately visible effect of this patch is that it fixes committing a temporary snapshot to its backing file. Previously, it would fail with a "permission denied" error because bdrv_inherited_flags() forced the backing file to be read-only, ignoring the r/w reopen of bdrv_commit(). The bigger problem this revealed is that the original open flags must actually only be applied to the temporary snapshot, and the original image file must be treated as a backing file of the temporary snapshot and get the right flags for that. Reported-by: Jan Kiszka Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz Signed-off-by: Stefan Hajnoczi --- block.c | 34 +++++++++++++++++++--------------- include/block/block.h | 2 +- tests/qemu-iotests/051 | 4 ++++ tests/qemu-iotests/051.out | 10 ++++++++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/block.c b/block.c index b749d31..c90c71a 100644 --- a/block.c +++ b/block.c @@ -775,6 +775,16 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) } /* + * Returns the flags that a temporary snapshot should get, based on the + * originally requested flags (the originally requested image will have flags + * like a backing file) + */ +static int bdrv_temp_snapshot_flags(int flags) +{ + return (flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; +} + +/* * Returns the flags that bs->file should get, based on the given flags for * the parent BDS */ @@ -787,11 +797,6 @@ static int bdrv_inherited_flags(int flags) * so we can enable both unconditionally on lower layers. */ flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP; - /* The backing file of a temporary snapshot is read-only */ - if (flags & BDRV_O_SNAPSHOT) { - flags &= ~BDRV_O_RDWR; - } - /* Clear flags that only apply to the top layer */ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); @@ -817,11 +822,6 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) { int open_flags = flags | BDRV_O_CACHE_WB; - /* The backing file of a temporary snapshot is read-only */ - if (flags & BDRV_O_SNAPSHOT) { - open_flags &= ~BDRV_O_RDWR; - } - /* * Clear flags that are internal to the block layer before opening the * image. @@ -1206,7 +1206,7 @@ done: return ret; } -void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp) +void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) { /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char *tmp_filename = g_malloc0(PATH_MAX + 1); @@ -1262,8 +1262,7 @@ void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp) bs_snapshot = bdrv_new("", &error_abort); ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, - (bs->open_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY, - bdrv_qcow2, &local_err); + flags, bdrv_qcow2, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1298,6 +1297,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, BlockDriverState *file = NULL, *bs; const char *drvname; Error *local_err = NULL; + int snapshot_flags = 0; assert(pbs); @@ -1358,6 +1358,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, if (flags & BDRV_O_RDWR) { flags |= BDRV_O_ALLOW_RDWR; } + if (flags & BDRV_O_SNAPSHOT) { + snapshot_flags = bdrv_temp_snapshot_flags(flags); + flags = bdrv_backing_flags(flags); + } assert(file == NULL); ret = bdrv_open_image(&file, filename, options, "file", @@ -1417,8 +1421,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, /* For snapshot=on, create a temporary qcow2 overlay. bs points to the * temporary snapshot afterwards. */ - if (flags & BDRV_O_SNAPSHOT) { - bdrv_append_temp_snapshot(bs, &local_err); + if (snapshot_flags) { + bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err); if (local_err) { error_propagate(errp, local_err); goto close_and_fail; diff --git a/include/block/block.h b/include/block/block.h index 27d8598..1b119aa 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -195,7 +195,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename, QDict *options, const char *bdref_key, int flags, bool allow_none, Error **errp); int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); -void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp); +void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp); int bdrv_open(BlockDriverState **pbs, const char *filename, const char *reference, QDict *options, int flags, BlockDriver *drv, Error **errp); diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 073dc7a..c4af131 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -233,6 +233,10 @@ echo 'qemu-io ide0-hd0 "write -P 0x22 0 4k"' | run_qemu -drive file="$TEST_IMG", $QEMU_IO -c "read -P 0x22 0 4k" "$TEST_IMG" | _filter_qemu_io +echo -e 'qemu-io ide0-hd0 "write -P 0x33 0 4k"\ncommit ide0-hd0' | run_qemu -drive file="$TEST_IMG",snapshot=on | _filter_qemu_io + +$QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 01b0384..31e329e 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -358,4 +358,14 @@ wrote 4096/4096 bytes at offset 0 read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qqeqemqemuqemu-qemu-iqemu-ioqemu-io qemu-io iqemu-io idqemu-io ideqemu-io ide0qemu-io ide0-qemu-io ide0-hqemu-io ide0-hdqemu-io ide0-hd0qemu-io ide0-hd0 qemu-io ide0-hd0 "qemu-io ide0-hd0 "wqemu-io ide0-hd0 "wrqemu-io ide0-hd0 "wriqemu-io ide0-hd0 "writqemu-io ide0-hd0 "writeqemu-io ide0-hd0 "write qemu-io ide0-hd0 "write -qemu-io ide0-hd0 "write -Pqemu-io ide0-hd0 "write -P qemu-io ide0-hd0 "write -P 0qemu-io ide0-hd0 "write -P 0xqemu-io ide0-hd0 "write -P 0x3qemu-io ide0-hd0 "write -P 0x33qemu-io ide0-hd0 "write -P 0x33 qemu-io ide0-hd0 "write -P 0x33 0qemu-io ide0-hd0 "write -P 0x33 0 qemu-io ide0-hd0 "write -P 0x33 0 4qemu-io ide0-hd0 "write -P 0x33 0 4kqemu-io ide0-hd0 "write -P 0x33 0 4k" +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +(qemu) ccocomcommcommicommitcommit commit icommit idcommit idecommit ide0commit ide0-commit ide0-hcommit ide0-hdcommit ide0-hd0 +(qemu) qququiquit + +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done -- cgit v1.1 From 02ce232c5051854bf49e6d2816c65e00f6d7e036 Mon Sep 17 00:00:00 2001 From: Kirill Batuzov Date: Tue, 6 May 2014 16:59:53 +0400 Subject: vl.c: remove init_clocks call from main Clocks are initialized in qemu_init_main_loop. They are not needed before it. Initializing them twice is not only unnecessary but is harmful: it results in memory leak and potentially can lead to a situation where different parts of QEMU use different sets of timers. To avoid it remove init_clocks call from main and add an assertion to qemu_clock_init that corresponding clock has not been initialized yet. Signed-off-by: Kirill Batuzov Signed-off-by: Stefan Hajnoczi --- qemu-timer.c | 3 +++ vl.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/qemu-timer.c b/qemu-timer.c index 9be1a41..00a5d35 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -126,6 +126,9 @@ static void qemu_clock_init(QEMUClockType type) { QEMUClock *clock = qemu_clock_ptr(type); + /* Assert that the clock of type TYPE has not been initialized yet. */ + assert(main_loop_tlg.tl[type] == NULL); + clock->type = type; clock->enabled = true; clock->last = INT64_MIN; diff --git a/vl.c b/vl.c index 73e0661..709d8cd 100644 --- a/vl.c +++ b/vl.c @@ -3024,7 +3024,6 @@ int main(int argc, char **argv, char **envp) runstate_init(); - init_clocks(); rtc_clock = QEMU_CLOCK_HOST; qemu_init_auxval(envp); -- cgit v1.1 From 4557117d9eed8cadc360aec23b42fc39a7011864 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Fri, 9 May 2014 12:08:10 +0200 Subject: gluster: Correctly propagate errors when volume isn't accessible The docs for glfs_init suggest that the function sets errno on every failure. In fact it doesn't. As other functions such as qemu_gluster_open() in the gluster block code report their errors based on this fact we need to make sure that errno is set on each failure. This fixes a crash of qemu-img/qemu when a gluster brick isn't accessible from given host while the server serving the volume description is. Thread 1 (Thread 0x7ffff7fba740 (LWP 203880)): #0 0x00007ffff77673f8 in glfs_lseek () from /usr/lib64/libgfapi.so.0 #1 0x0000555555574a68 in qemu_gluster_getlength () #2 0x0000555555565742 in refresh_total_sectors () #3 0x000055555556914f in bdrv_open_common () #4 0x000055555556e8e8 in bdrv_open () #5 0x000055555556f02f in bdrv_open_image () #6 0x000055555556e5f6 in bdrv_open () #7 0x00005555555c5775 in bdrv_new_open () #8 0x00005555555c5b91 in img_info () #9 0x00007ffff62c9c05 in __libc_start_main () from /lib64/libc.so.6 #10 0x00005555555648ad in _start () Signed-off-by: Stefan Hajnoczi --- block/gluster.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/block/gluster.c b/block/gluster.c index 8836085..d0726ec 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -207,6 +207,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename, "volume=%s image=%s transport=%s", gconf->server, gconf->port, gconf->volname, gconf->image, gconf->transport); + + /* glfs_init sometimes doesn't set errno although docs suggest that */ + if (errno == 0) + errno = EINVAL; + goto out; } return glfs; @@ -482,7 +487,7 @@ static int qemu_gluster_create(const char *filename, glfs = qemu_gluster_init(gconf, filename, errp); if (!glfs) { - ret = -EINVAL; + ret = -errno; goto out; } -- cgit v1.1 From 4f11aa8a40351b28c0e67c7276e0003b38cc46ac Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 8 May 2014 20:57:55 +0200 Subject: block/raw-posix: Try both FIEMAP and SEEK_HOLE The current version of raw-posix always uses ioctl(FS_IOC_FIEMAP) if FIEMAP is available; lseek with SEEK_HOLE/SEEK_DATA are not even compiled in in this case. However, there may be implementations which support the latter but not the former (e.g., NFSv4.2) as well as vice versa. To cover both cases, try FIEMAP first (as this will return -ENOTSUP if not supported instead of returning a failsafe value (everything allocated as a single extent)) and if that does not work, fall back to SEEK_HOLE/SEEK_DATA. Signed-off-by: Max Reitz Signed-off-by: Stefan Hajnoczi --- block/raw-posix.c | 127 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 50 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index 3ce026d..6586a0c 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -146,6 +146,9 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; bool discard_zeroes:1; +#ifdef CONFIG_FIEMAP + bool skip_fiemap; +#endif } BDRVRawState; typedef struct BDRVRawReopenState { @@ -1272,53 +1275,29 @@ static int raw_create(const char *filename, QEMUOptionParameter *options, return result; } -/* - * Returns true iff the specified sector is present in the disk image. Drivers - * not implementing the functionality are assumed to not support backing files, - * hence all their sectors are reported as allocated. - * - * If 'sector_num' is beyond the end of the disk image the return value is 0 - * and 'pnum' is set to 0. - * - * 'pnum' is set to the number of sectors (including and immediately following - * the specified sector) that are known to be in the same - * allocated/unallocated state. - * - * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes - * beyond the end of the disk image it will be clamped. - */ -static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, - int64_t sector_num, - int nb_sectors, int *pnum) +static int64_t try_fiemap(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole, int nb_sectors, int *pnum) { - off_t start, data, hole; - int64_t ret; - - ret = fd_open(bs); - if (ret < 0) { - return ret; - } - - start = sector_num * BDRV_SECTOR_SIZE; - ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; - #ifdef CONFIG_FIEMAP - BDRVRawState *s = bs->opaque; + int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; struct { struct fiemap fm; struct fiemap_extent fe; } f; + if (s->skip_fiemap) { + return -ENOTSUP; + } + f.fm.fm_start = start; f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE; f.fm.fm_flags = 0; f.fm.fm_extent_count = 1; f.fm.fm_reserved = 0; if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) { - /* Assume everything is allocated. */ - *pnum = nb_sectors; - return ret; + s->skip_fiemap = true; + return -errno; } if (f.fm.fm_mapped_extents == 0) { @@ -1326,44 +1305,92 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, * f.fm.fm_start + f.fm.fm_length must be clamped to the file size! */ off_t length = lseek(s->fd, 0, SEEK_END); - hole = f.fm.fm_start; - data = MIN(f.fm.fm_start + f.fm.fm_length, length); + *hole = f.fm.fm_start; + *data = MIN(f.fm.fm_start + f.fm.fm_length, length); } else { - data = f.fe.fe_logical; - hole = f.fe.fe_logical + f.fe.fe_length; + *data = f.fe.fe_logical; + *hole = f.fe.fe_logical + f.fe.fe_length; if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) { ret |= BDRV_BLOCK_ZERO; } } -#elif defined SEEK_HOLE && defined SEEK_DATA + return ret; +#else + return -ENOTSUP; +#endif +} +static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data, + off_t *hole, int *pnum) +{ +#if defined SEEK_HOLE && defined SEEK_DATA BDRVRawState *s = bs->opaque; - hole = lseek(s->fd, start, SEEK_HOLE); - if (hole == -1) { + *hole = lseek(s->fd, start, SEEK_HOLE); + if (*hole == -1) { /* -ENXIO indicates that sector_num was past the end of the file. * There is a virtual hole there. */ assert(errno != -ENXIO); - /* Most likely EINVAL. Assume everything is allocated. */ - *pnum = nb_sectors; - return ret; + return -errno; } - if (hole > start) { - data = start; + if (*hole > start) { + *data = start; } else { /* On a hole. We need another syscall to find its end. */ - data = lseek(s->fd, start, SEEK_DATA); - if (data == -1) { - data = lseek(s->fd, 0, SEEK_END); + *data = lseek(s->fd, start, SEEK_DATA); + if (*data == -1) { + *data = lseek(s->fd, 0, SEEK_END); } } + + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; #else - data = 0; - hole = start + nb_sectors * BDRV_SECTOR_SIZE; + return -ENOTSUP; #endif +} + +/* + * Returns true iff the specified sector is present in the disk image. Drivers + * not implementing the functionality are assumed to not support backing files, + * hence all their sectors are reported as allocated. + * + * If 'sector_num' is beyond the end of the disk image the return value is 0 + * and 'pnum' is set to 0. + * + * 'pnum' is set to the number of sectors (including and immediately following + * the specified sector) that are known to be in the same + * allocated/unallocated state. + * + * 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes + * beyond the end of the disk image it will be clamped. + */ +static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) +{ + off_t start, data = 0, hole = 0; + int64_t ret; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + start = sector_num * BDRV_SECTOR_SIZE; + + ret = try_fiemap(bs, start, &data, &hole, nb_sectors, pnum); + if (ret < 0) { + ret = try_seek_hole(bs, start, &data, &hole, pnum); + if (ret < 0) { + /* Assume everything is allocated. */ + data = 0; + hole = start + nb_sectors * BDRV_SECTOR_SIZE; + ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start; + } + } if (data <= start) { /* On a data extent, compute sectors to the end of the extent. */ -- cgit v1.1 From e940bc13ee15d6304f3a467961b9f33fc0f25595 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Wed, 30 Apr 2014 10:55:08 -0400 Subject: block: qemu-iotests - add common.qemu, for bash-controlled qemu tests This creates some common functions for bash language qemu-iotests to control, and communicate with, a running QEMU process. 4 functions are introduced: 1. _launch_qemu() This launches the QEMU process(es), and sets up the file descriptors and fifos for communication. You can choose to launch each QEMU process listening for either QMP or HMP monitor. You can call this function multiple times, and save the handle returned from each. The returned handle is in $QEMU_HANDLE. You must copy this value. Commands 2 and 3 use the handle received from _launch_qemu(), to talk to the appropriate process. 2. _send_qemu_cmd() Sends a command string, specified by $2, to QEMU. If $3 is non-NULL, _send_qemu_cmd() will wait to receive $3 as a required result string from QEMU. Failure to receive $3 will cause the test to fail. The command can optionally be retried $qemu_cmd_repeat number of times. Set $qemu_error_no_exit to not force the test the fail on exit; in this case, $QEMU_STATUS[$1] will be set to -1 on failure. 3. _timed_wait_for() Waits for a response, for up to a default of 10 seconds. If $2 is not seen in that time (anywhere in the response), then the test fails. Primarily used by _send_qemu_cmd, but could be useful standalone, as well. To prevent automatic exit (and therefore test failure), set $qemu_error_no_exit to a non-NULL value. If $silent is a non-NULL value, then output to stdout will be suppressed. 4. _cleanup_qemu() Kills the running QEMU processes, and removes the fifos. Signed-off-by: Jeff Cody Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/common.qemu | 200 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 tests/qemu-iotests/common.qemu diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu new file mode 100644 index 0000000..918af31 --- /dev/null +++ b/tests/qemu-iotests/common.qemu @@ -0,0 +1,200 @@ +#!/bin/bash +# +# This allows for launching of multiple QEMU instances, with independent +# communication possible to each instance. +# +# Each instance can choose, at launch, to use either the QMP or the +# HMP (monitor) interface. +# +# All instances are cleaned up via _cleanup_qemu, including killing the +# running qemu instance. +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +QEMU_COMM_TIMEOUT=10 + +QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$" +QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$" + +QEMU_PID= +_QEMU_HANDLE=0 +QEMU_HANDLE=0 + +# If bash version is >= 4.1, these will be overwritten and dynamic +# file descriptor values assigned. +_out_fd=3 +_in_fd=4 + +# Wait for expected QMP response from QEMU. Will time out +# after 10 seconds, which counts as failure. +# +# Override QEMU_COMM_TIMEOUT for a timeout different than the +# default 10 seconds +# +# $1: The handle to use +# $2+ All remaining arguments comprise the string to search for +# in the response. +# +# If $silent is set to anything but an empty string, then +# response is not echoed out. +function _timed_wait_for() +{ + local h=${1} + shift + + QEMU_STATUS[$h]=0 + while read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} + do + if [ -z "${silent}" ]; then + echo "${resp}" | _filter_testdir | _filter_qemu \ + | _filter_qemu_io | _filter_qmp + fi + grep -q "${*}" < <(echo ${resp}) + if [ $? -eq 0 ]; then + return + fi + done + QEMU_STATUS[$h]=-1 + if [ -z "${qemu_error_no_exit}" ]; then + echo "Timeout waiting for ${*} on handle ${h}" + exit 1 # Timeout means the test failed + fi +} + + +# Sends QMP or HMP command to QEMU, and waits for the expected response +# +# $1: QEMU handle to use +# $2: String of the QMP command to send +# ${@: -1} (Last string passed) +# String that the QEMU response should contain. If it is a null +# string, do not wait for a response +# +# Set qemu_cmd_repeat to the number of times to repeat the cmd +# until either timeout, or a response. If it is not set, or <=0, +# then the command is only sent once. +# +# If $qemu_error_no_exit is set, then even if the expected response +# is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in +# that case. +function _send_qemu_cmd() +{ + local h=${1} + local count=1 + local cmd= + local use_error=${qemu_error_no_exit} + shift + + if [ ${qemu_cmd_repeat} -gt 0 ] 2>/dev/null; then + count=${qemu_cmd_repeat} + use_error="no" + fi + # This array element extraction is done to accomodate pathnames with spaces + cmd=${@: 1:${#@}-1} + shift $(($# - 1)) + + while [ ${count} -gt 0 ] + do + echo "${cmd}" >&${QEMU_IN[${h}]} + if [ -n "${1}" ]; then + qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" + if [ ${QEMU_STATUS[$h]} -eq 0 ]; then + return + fi + fi + let count--; + done + if [ ${QEMU_STATUS[$h]} -ne 0 ] && [ -z "${qemu_error_no_exit}" ]; then + echo "Timeout waiting for ${1} on handle ${h}" + exit 1 #Timeout means the test failed + fi +} + + +# Launch a QEMU process. +# +# Input parameters: +# $qemu_comm_method: set this variable to 'monitor' (case insensitive) +# to use the QEMU HMP monitor for communication. +# Otherwise, the default of QMP is used. +# Returns: +# $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. +# +function _launch_qemu() +{ + local comm= + local fifo_out= + local fifo_in= + + if (shopt -s nocasematch; [[ "${qemu_comm_method}" == "monitor" ]]) + then + comm="-monitor stdio" + else + local qemu_comm_method="qmp" + comm="-monitor none -qmp stdio" + fi + + fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE} + fifo_in=${QEMU_FIFO_IN}_${_QEMU_HANDLE} + mkfifo "${fifo_out}" + mkfifo "${fifo_in}" + + "${QEMU}" -nographic -serial none ${comm} -machine accel=qtest "${@}" 2>&1 \ + >"${fifo_out}" \ + <"${fifo_in}" & + QEMU_PID[${_QEMU_HANDLE}]=$! + + if [[ "${BASH_VERSINFO[0]}" -ge "5" || + ("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]] + then + # bash >= 4.1 required for automatic fd + exec {_out_fd}<"${fifo_out}" + exec {_in_fd}>"${fifo_in}" + else + let _out_fd++ + let _in_fd++ + eval "exec ${_out_fd}<'${fifo_out}'" + eval "exec ${_in_fd}>'${fifo_in}'" + fi + + QEMU_OUT[${_QEMU_HANDLE}]=${_out_fd} + QEMU_IN[${_QEMU_HANDLE}]=${_in_fd} + QEMU_STATUS[${_QEMU_HANDLE}]=0 + + if [ "${qemu_comm_method}" == "qmp" ] + then + # Don't print response, since it has version information in it + silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities" + fi + QEMU_HANDLE=${_QEMU_HANDLE} + let _QEMU_HANDLE++ +} + + +# Silenty kills the QEMU process +function _cleanup_qemu() +{ + # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices + for i in "${!QEMU_OUT[@]}" + do + kill -KILL ${QEMU_PID[$i]} 2>/dev/null + wait ${QEMU_PID[$i]} 2>/dev/null # silent kill + rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}" + eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors + eval "exec ${QEMU_OUT[$i]}<&-" + done +} -- cgit v1.1 From e86e8697708ada85ce4b8f9df00342912f7ca14c Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Wed, 30 Apr 2014 10:55:09 -0400 Subject: block: qemu-iotests - update 085 to use common.qemu The new functionality of common.qemu implements the QEMU control and communication functionality that was originally in test 085. This removes that now-duplicate functionality, and uses the common.qemu functions. The QEMU commandline changes slightly due to this; in addition to monitor and qmp i/o options, the new QEMU commandline from inside common.qemu now introduces -machine accel=qtest. Reviewed-by: Benoit Canet Reviewed-by: Fam Zheng Signed-off-by: Jeff Cody Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/085 | 73 +++++++++----------------------------------------- 1 file changed, 12 insertions(+), 61 deletions(-) diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index 33c8dc4..56cd6f8 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -30,10 +30,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -qemu_pid= - -QMP_IN="${TEST_DIR}/qmp-in-$$" -QMP_OUT="${TEST_DIR}/qmp-out-$$" snapshot_virt0="snapshot-v0.qcow2" snapshot_virt1="snapshot-v1.qcow2" @@ -42,10 +38,7 @@ MAX_SNAPSHOTS=10 _cleanup() { - kill -KILL ${qemu_pid} - wait ${qemu_pid} 2>/dev/null # silent kill - - rm -f "${QMP_IN}" "${QMP_OUT}" + _cleanup_qemu for i in $(seq 1 ${MAX_SNAPSHOTS}) do rm -f "${TEST_DIR}/${i}-${snapshot_virt0}" @@ -59,43 +52,12 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # get standard environment, filters and checks . ./common.rc . ./common.filter +. ./common.qemu _supported_fmt qcow2 _supported_proto file _supported_os Linux -# Wait for expected QMP response from QEMU. Will time out -# after 10 seconds, which counts as failure. -# -# $1 is the string to expect -# -# If $silent is set to anything but an empty string, then -# response is not echoed out. -function timed_wait_for() -{ - while read -t 10 resp <&5 - do - if [ "${silent}" == "" ]; then - echo "${resp}" | _filter_testdir | _filter_qemu - fi - grep -q "${1}" < <(echo ${resp}) - if [ $? -eq 0 ]; then - return - fi - done - echo "Timeout waiting for ${1}" - exit 1 # Timeout means the test failed -} - -# Sends QMP command to QEMU, and waits for the expected response -# -# ${1}: String of the QMP command to send -# ${2}: String that the QEMU response should contain -function send_qmp_cmd() -{ - echo "${1}" >&6 - timed_wait_for "${2}" -} # ${1}: unique identifier for the snapshot filename function create_single_snapshot() @@ -104,7 +66,7 @@ function create_single_snapshot() 'arguments': { 'device': 'virtio0', 'snapshot-file':'"${TEST_DIR}/${1}-${snapshot_virt0}"', 'format': 'qcow2' } }" - send_qmp_cmd "${cmd}" "return" + _send_qemu_cmd $h "${cmd}" "return" } # ${1}: unique identifier for the snapshot filename @@ -120,14 +82,11 @@ function create_group_snapshot() 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt1}"' } } ] } }" - send_qmp_cmd "${cmd}" "return" + _send_qemu_cmd $h "${cmd}" "return" } size=128M -mkfifo "${QMP_IN}" -mkfifo "${QMP_OUT}" - _make_test_img $size mv "${TEST_IMG}" "${TEST_IMG}.orig" _make_test_img $size @@ -136,23 +95,15 @@ echo echo === Running QEMU === echo -"${QEMU}" -nographic -monitor none -serial none -qmp stdio\ - -drive file="${TEST_IMG}.orig",if=virtio\ - -drive file="${TEST_IMG}",if=virtio 2>&1 >"${QMP_OUT}" <"${QMP_IN}"& -qemu_pid=$! - -# redirect fifos to file descriptors, to keep from blocking -exec 5<"${QMP_OUT}" -exec 6>"${QMP_IN}" - -# Don't print response, since it has version information in it -silent=yes timed_wait_for "capabilities" +qemu_comm_method="qmp" +_launch_qemu -drive file="${TEST_IMG}.orig",if=virtio -drive file="${TEST_IMG}",if=virtio +h=$QEMU_HANDLE echo echo === Sending capabilities === echo -send_qmp_cmd "{ 'execute': 'qmp_capabilities' }" "return" +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" echo echo === Create a single snapshot on virtio0 === @@ -165,16 +116,16 @@ echo echo === Invalid command - missing device and nodename === echo -send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync', - 'arguments': { 'snapshot-file':'"${TEST_DIR}"/1-${snapshot_virt0}', +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'snapshot-file':'"${TEST_DIR}/1-${snapshot_virt0}"', 'format': 'qcow2' } }" "error" echo echo === Invalid command - missing snapshot-file === echo -send_qmp_cmd "{ 'execute': 'blockdev-snapshot-sync', - 'arguments': { 'device': 'virtio0', +_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync', + 'arguments': { 'device': 'virtio0', 'format': 'qcow2' } }" "error" echo echo -- cgit v1.1 From fd040174ac7c302365c1d6dc5646f957972d67d6 Mon Sep 17 00:00:00 2001 From: Jeff Cody Date: Wed, 30 Apr 2014 10:55:10 -0400 Subject: block: qemu-iotests - test for live migration This is an initial, simple live migration test from one running VM to another, using monitor commands. This is also an example of using the new common.qemu functions for controlling multiple running qemu instances, for tests that need a live qemu vm. Reviewed-by: Fam Zheng Signed-off-by: Jeff Cody Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/091 | 105 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/091.out | 28 ++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 134 insertions(+) create mode 100755 tests/qemu-iotests/091 create mode 100644 tests/qemu-iotests/091.out diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 new file mode 100755 index 0000000..384b3ac --- /dev/null +++ b/tests/qemu-iotests/091 @@ -0,0 +1,105 @@ +#!/bin/bash +# +# Live migration test +# +# Performs a migration from one VM to another via monitor commands +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +MIG_FIFO="${TEST_DIR}/migrate" + +_cleanup() +{ + rm -f "${MIG_FIFO}" + _cleanup_qemu + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +size=1G + +_make_test_img $size + +mkfifo "${MIG_FIFO}" + +echo +echo === Starting QEMU VM1 === +echo + +qemu_comm_method="monitor" +_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk +h1=$QEMU_HANDLE + +echo +echo === Starting QEMU VM2 === +echo +_launch_qemu -drive file="${TEST_IMG}",cache=none,id=disk \ + -incoming "exec: cat '${MIG_FIFO}'" +h2=$QEMU_HANDLE + +echo +echo === VM 1: Migrate from VM1 to VM2 === +echo + +silent=yes +_send_qemu_cmd $h1 'qemu-io disk "write -P 0x22 0 4M"' "(qemu)" +echo "vm1: qemu-io disk write complete" +_send_qemu_cmd $h1 "migrate \"exec: cat > '${MIG_FIFO}'\"" "(qemu)" +echo "vm1: live migration started" +qemu_cmd_repeat=20 _send_qemu_cmd $h1 "info migrate" "completed" +echo "vm1: live migration completed" + +echo +echo === VM 2: Post-migration, write to disk, verify running === +echo + +_send_qemu_cmd $h2 'qemu-io disk "write 4M 1M"' "(qemu)" +echo "vm2: qemu-io disk write complete" +qemu_cmd_repeat=20 _send_qemu_cmd $h2 "info status" "running" +echo "vm2: qemu process running successfully" + +echo "vm2: flush io, and quit" +_send_qemu_cmd $h2 'qemu-io disk flush' "(qemu)" +_send_qemu_cmd $h2 'quit' "" + +echo "Check image pattern" +${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io + +echo "Running 'qemu-img check -r all \$TEST_IMG'" +"${QEMU_IMG}" check -r all "${TEST_IMG}" 2>&1 | _filter_testdir | _filter_qemu + +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/091.out b/tests/qemu-iotests/091.out new file mode 100644 index 0000000..a2e0122 --- /dev/null +++ b/tests/qemu-iotests/091.out @@ -0,0 +1,28 @@ +QA output created by 091 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 + +=== Starting QEMU VM1 === + + +=== Starting QEMU VM2 === + + +=== VM 1: Migrate from VM1 to VM2 === + +vm1: qemu-io disk write complete +vm1: live migration started +vm1: live migration completed + +=== VM 2: Post-migration, write to disk, verify running === + +vm2: qemu-io disk write complete +vm2: qemu process running successfully +vm2: flush io, and quit +Check image pattern +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Running 'qemu-img check -r all $TEST_IMG' +No errors were found on the image. +80/16384 = 0.49% allocated, 0.00% fragmented, 0.00% compressed clusters +Image end offset: 5570560 +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index ae09663..cd3e4d2 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -96,3 +96,4 @@ 087 rw auto 088 rw auto 090 rw auto quick +091 rw auto -- cgit v1.1 From 5a007547df76446ab891df93ebc55749716609bf Mon Sep 17 00:00:00 2001 From: Sangho Park Date: Thu, 8 May 2014 12:47:10 +0400 Subject: glib: fix g_poll early timeout on windows g_poll has a problem on Windows when using timeouts < 10ms, in glib/gpoll.c: /* If not, and we have a significant timeout, poll again with * timeout then. Note that this will return indication for only * one event, or only for messages. We ignore timeouts less than * ten milliseconds as they are mostly pointless on Windows, the * MsgWaitForMultipleObjectsEx() call will timeout right away * anyway. */ if (retval == 0 && (timeout == INFINITE || timeout >= 10)) retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); so whenever g_poll is called with timeout < 10ms it does a quick poll instead of wait, this causes significant performance degradation of QEMU, thus we should use WaitForMultipleObjectsEx directly Signed-off-by: Stanislav Vorobiov Signed-off-by: Stefan Hajnoczi --- include/glib-compat.h | 9 +++- util/oslib-win32.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/include/glib-compat.h b/include/glib-compat.h index 8d25900..1280fb2 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -24,7 +24,14 @@ static inline guint g_timeout_add_seconds(guint interval, GSourceFunc function, } #endif -#if !GLIB_CHECK_VERSION(2, 20, 0) +#ifdef _WIN32 +/* + * g_poll has a problem on Windows when using + * timeouts < 10ms, so use wrapper. + */ +#define g_poll(fds, nfds, timeout) g_poll_fixed(fds, nfds, timeout) +gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout); +#elif !GLIB_CHECK_VERSION(2, 20, 0) /* * Glib before 2.20.0 doesn't implement g_poll, so wrap it to compile properly * on older systems. diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 93f7d35..69552f7 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -238,3 +238,115 @@ char *qemu_get_exec_dir(void) { return g_strdup(exec_dir); } + +/* + * g_poll has a problem on Windows when using + * timeouts < 10ms, in glib/gpoll.c: + * + * // If not, and we have a significant timeout, poll again with + * // timeout then. Note that this will return indication for only + * // one event, or only for messages. We ignore timeouts less than + * // ten milliseconds as they are mostly pointless on Windows, the + * // MsgWaitForMultipleObjectsEx() call will timeout right away + * // anyway. + * + * if (retval == 0 && (timeout == INFINITE || timeout >= 10)) + * retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + * + * So whenever g_poll is called with timeout < 10ms it does + * a quick poll instead of wait, this causes significant performance + * degradation of QEMU, thus we should use WaitForMultipleObjectsEx + * directly + */ +gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout) +{ + guint i; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + gint nhandles = 0; + int num_completed = 0; + + for (i = 0; i < nfds; i++) { + gint j; + + if (fds[i].fd <= 0) { + continue; + } + + /* don't add same handle several times + */ + for (j = 0; j < nhandles; j++) { + if (handles[j] == (HANDLE)fds[i].fd) { + break; + } + } + + if (j == nhandles) { + if (nhandles == MAXIMUM_WAIT_OBJECTS) { + fprintf(stderr, "Too many handles to wait for!\n"); + break; + } else { + handles[nhandles++] = (HANDLE)fds[i].fd; + } + } + } + + for (i = 0; i < nfds; ++i) { + fds[i].revents = 0; + } + + if (timeout == -1) { + timeout = INFINITE; + } + + if (nhandles == 0) { + if (timeout == INFINITE) { + return -1; + } else { + SleepEx(timeout, TRUE); + return 0; + } + } + + while (1) { + DWORD res; + gint j; + + res = WaitForMultipleObjectsEx(nhandles, handles, FALSE, + timeout, TRUE); + + if (res == WAIT_FAILED) { + for (i = 0; i < nfds; ++i) { + fds[i].revents = 0; + } + + return -1; + } else if ((res == WAIT_TIMEOUT) || (res == WAIT_IO_COMPLETION) || + ((int)res < (int)WAIT_OBJECT_0) || + (res >= (WAIT_OBJECT_0 + nhandles))) { + break; + } + + for (i = 0; i < nfds; ++i) { + if (handles[res - WAIT_OBJECT_0] == (HANDLE)fds[i].fd) { + fds[i].revents = fds[i].events; + } + } + + ++num_completed; + + if (nhandles <= 1) { + break; + } + + /* poll the rest of the handles + */ + for (j = res - WAIT_OBJECT_0 + 1; j < nhandles; j++) { + handles[j - 1] = handles[j]; + } + --nhandles; + + timeout = 0; + } + + return num_completed; +} -- cgit v1.1