aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-03-13 15:33:04 +0000
committerPeter Maydell <peter.maydell@linaro.org>2014-03-13 15:33:04 +0000
commit57fac92c2d4487d5c45e1ca96df6790f96c9e64c (patch)
treebd6d23260932db3f7663102a5222bc0b6d5bffe2 /tests
parent41975b269cf1503c735f8233f8ef373d74f1f137 (diff)
parent57ed25b1b08a43f29326df064d43b6420a23b5ba (diff)
Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging
Block pull request # gpg: Signature made Thu 13 Mar 2014 13:50:49 GMT using RSA key ID 81AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35 775A 9CA4 ABB3 81AB 73C8 * remotes/stefanha/tags/block-pull-request: (24 commits) block/raw-win32: bdrv_parse_filename() for hdev block/raw-posix: Strip protocol prefix on creation block/raw-posix: bdrv_parse_filename() for cdrom block/raw-posix: bdrv_parse_filename() for floppy block/raw-posix: bdrv_parse_filename() for hdev qemu-io: Fix warnings from static code analysis block: Unlink temporary file qcow2: Don't write with BDRV_O_INCOMING qcow2: Keep option in qcow2_invalidate_cache() qmp: add query-iothreads command iothread: stash thread ID away dataplane: replace internal thread with IOThread iothread: add "iothread" qdev property type qdev: make get_pointer() handle temporary strings iothread: add I/O thread object aio: add aio_context_acquire() and aio_context_release() rfifolock: add recursive FIFO lock object: add object_get_canonical_path_component() block: Rewrite the snapshot authorization mechanism for block filters. iotests: Test corruption during COW request ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile2
-rwxr-xr-xtests/qemu-iotests/06026
-rw-r--r--tests/qemu-iotests/060.out15
-rw-r--r--tests/test-aio.c59
-rw-r--r--tests/test-rfifolock.c91
5 files changed, 193 insertions, 0 deletions
diff --git a/tests/Makefile b/tests/Makefile
index 7bc3999ecc..471b4c8785 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -35,6 +35,7 @@ check-unit-y += tests/test-visitor-serialization$(EXESUF)
check-unit-y += tests/test-iov$(EXESUF)
gcov-files-test-iov-y = util/iov.c
check-unit-y += tests/test-aio$(EXESUF)
+check-unit-y += tests/test-rfifolock$(EXESUF)
check-unit-y += tests/test-throttle$(EXESUF)
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
@@ -193,6 +194,7 @@ tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
+tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o libqemuutil.a libqemustub.a
tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060
index af8ed9f39a..f0116aab1d 100755
--- a/tests/qemu-iotests/060
+++ b/tests/qemu-iotests/060
@@ -138,6 +138,32 @@ $QEMU_IMG snapshot -a foo "$TEST_IMG"
_check_test_img
$QEMU_IO -c "$OPEN_RO" -c "read -P 1 0 512" | _filter_qemu_io
+echo
+echo "=== Testing overlap while COW is in flight ==="
+echo
+# compat=0.10 is required in order to make the following discard actually
+# unallocate the sector rather than make it a zero sector - we want COW, after
+# all.
+IMGOPTS='compat=0.10' _make_test_img 1G
+# Write two clusters, the second one enforces creation of an L2 table after
+# the first data cluster.
+$QEMU_IO -c 'write 0k 64k' -c 'write 512M 64k' "$TEST_IMG" | _filter_qemu_io
+# Discard the first cluster. This cluster will soon enough be reallocated and
+# used for COW.
+$QEMU_IO -c 'discard 0k 64k' "$TEST_IMG" | _filter_qemu_io
+# Now, corrupt the image by marking the second L2 table cluster as free.
+poke_file "$TEST_IMG" '131084' "\x00\x00" # 0x2000c
+# Start a write operation requiring COW on the image stopping it right before
+# doing the read; then, trigger the corruption prevention by writing anything to
+# any unallocated cluster, leading to an attempt to overwrite the second L2
+# table. Finally, resume the COW write and see it fail (but not crash).
+echo "open -o file.driver=blkdebug $TEST_IMG
+break cow_read 0
+aio_write 0k 1k
+wait_break 0
+write 64k 64k
+resume 0" | $QEMU_IO | _filter_qemu_io
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out
index 6c7bdbb2f2..a517948036 100644
--- a/tests/qemu-iotests/060.out
+++ b/tests/qemu-iotests/060.out
@@ -78,4 +78,19 @@ read 512/512 bytes at offset 0
No errors were found on the image.
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing overlap while COW is in flight ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 536870912
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+discard 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qcow2: Preventing invalid write on metadata (overlaps with active L2 table); image marked as corrupt.
+blkdebug: Suspended request '0'
+write failed: Input/output error
+blkdebug: Resuming request '0'
+aio_write failed: No medium found
*** done
diff --git a/tests/test-aio.c b/tests/test-aio.c
index 592721ed3f..56f4288ca8 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -112,6 +112,64 @@ static void test_notify(void)
g_assert(!aio_poll(ctx, false));
}
+typedef struct {
+ QemuMutex start_lock;
+ bool thread_acquired;
+} AcquireTestData;
+
+static void *test_acquire_thread(void *opaque)
+{
+ AcquireTestData *data = opaque;
+
+ /* Wait for other thread to let us start */
+ qemu_mutex_lock(&data->start_lock);
+ qemu_mutex_unlock(&data->start_lock);
+
+ aio_context_acquire(ctx);
+ aio_context_release(ctx);
+
+ data->thread_acquired = true; /* success, we got here */
+
+ return NULL;
+}
+
+static void dummy_notifier_read(EventNotifier *unused)
+{
+ g_assert(false); /* should never be invoked */
+}
+
+static void test_acquire(void)
+{
+ QemuThread thread;
+ EventNotifier notifier;
+ AcquireTestData data;
+
+ /* Dummy event notifier ensures aio_poll() will block */
+ event_notifier_init(&notifier, false);
+ aio_set_event_notifier(ctx, &notifier, dummy_notifier_read);
+ g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */
+
+ qemu_mutex_init(&data.start_lock);
+ qemu_mutex_lock(&data.start_lock);
+ data.thread_acquired = false;
+
+ qemu_thread_create(&thread, "test_acquire_thread",
+ test_acquire_thread,
+ &data, QEMU_THREAD_JOINABLE);
+
+ /* Block in aio_poll(), let other thread kick us and acquire context */
+ aio_context_acquire(ctx);
+ qemu_mutex_unlock(&data.start_lock); /* let the thread run */
+ g_assert(!aio_poll(ctx, true));
+ aio_context_release(ctx);
+
+ qemu_thread_join(&thread);
+ aio_set_event_notifier(ctx, &notifier, NULL);
+ event_notifier_cleanup(&notifier);
+
+ g_assert(data.thread_acquired);
+}
+
static void test_bh_schedule(void)
{
BHTestData data = { .n = 0 };
@@ -775,6 +833,7 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
g_test_add_func("/aio/notify", test_notify);
+ g_test_add_func("/aio/acquire", test_acquire);
g_test_add_func("/aio/bh/schedule", test_bh_schedule);
g_test_add_func("/aio/bh/schedule10", test_bh_schedule10);
g_test_add_func("/aio/bh/cancel", test_bh_cancel);
diff --git a/tests/test-rfifolock.c b/tests/test-rfifolock.c
new file mode 100644
index 0000000000..0572ebb42a
--- /dev/null
+++ b/tests/test-rfifolock.c
@@ -0,0 +1,91 @@
+/*
+ * RFifoLock tests
+ *
+ * Copyright Red Hat, Inc. 2013
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <glib.h>
+#include "qemu-common.h"
+#include "qemu/rfifolock.h"
+
+static void test_nesting(void)
+{
+ RFifoLock lock;
+
+ /* Trivial test, ensure the lock is recursive */
+ rfifolock_init(&lock, NULL, NULL);
+ rfifolock_lock(&lock);
+ rfifolock_lock(&lock);
+ rfifolock_lock(&lock);
+ rfifolock_unlock(&lock);
+ rfifolock_unlock(&lock);
+ rfifolock_unlock(&lock);
+ rfifolock_destroy(&lock);
+}
+
+typedef struct {
+ RFifoLock lock;
+ int fd[2];
+} CallbackTestData;
+
+static void rfifolock_cb(void *opaque)
+{
+ CallbackTestData *data = opaque;
+ int ret;
+ char c = 0;
+
+ ret = write(data->fd[1], &c, sizeof(c));
+ g_assert(ret == 1);
+}
+
+static void *callback_thread(void *opaque)
+{
+ CallbackTestData *data = opaque;
+
+ /* The other thread holds the lock so the contention callback will be
+ * invoked...
+ */
+ rfifolock_lock(&data->lock);
+ rfifolock_unlock(&data->lock);
+ return NULL;
+}
+
+static void test_callback(void)
+{
+ CallbackTestData data;
+ QemuThread thread;
+ int ret;
+ char c;
+
+ rfifolock_init(&data.lock, rfifolock_cb, &data);
+ ret = qemu_pipe(data.fd);
+ g_assert(ret == 0);
+
+ /* Hold lock but allow the callback to kick us by writing to the pipe */
+ rfifolock_lock(&data.lock);
+ qemu_thread_create(&thread, "callback_thread",
+ callback_thread, &data, QEMU_THREAD_JOINABLE);
+ ret = read(data.fd[0], &c, sizeof(c));
+ g_assert(ret == 1);
+ rfifolock_unlock(&data.lock);
+ /* If we got here then the callback was invoked, as expected */
+
+ qemu_thread_join(&thread);
+ close(data.fd[0]);
+ close(data.fd[1]);
+ rfifolock_destroy(&data.lock);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/nesting", test_nesting);
+ g_test_add_func("/callback", test_callback);
+ return g_test_run();
+}