aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.include27
-rw-r--r--tests/boot-serial-test.c69
-rwxr-xr-xtests/docker/test-full79
-rw-r--r--tests/qapi-schema/doc-bad-section.err0
-rw-r--r--tests/qapi-schema/doc-bad-section.exit1
-rw-r--r--tests/qapi-schema/doc-bad-section.json11
-rw-r--r--tests/qapi-schema/doc-bad-section.out13
-rw-r--r--tests/qapi-schema/doc-good.json1
-rw-r--r--tests/qapi-schema/doc-good.out4
-rw-r--r--tests/qapi-schema/doc-good.texi11
-rw-r--r--tests/qapi-schema/test-qapi.py6
-rwxr-xr-xtests/qemu-iotests/1974
-rwxr-xr-xtests/qemu-iotests/20295
-rw-r--r--tests/qemu-iotests/202.out11
-rwxr-xr-xtests/qemu-iotests/20359
-rw-r--r--tests/qemu-iotests/203.out6
-rw-r--r--tests/qemu-iotests/common.filter3
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/qemu-iotests/iotests.py5
-rw-r--r--tests/tcg/xtensa/test_sr.S2
-rw-r--r--tests/test-aio-multithread.c1
-rw-r--r--tests/test-bdrv-drain.c651
-rw-r--r--tests/test-char.c17
-rw-r--r--tests/test-clone-visitor.c1
-rw-r--r--tests/test-hbitmap.c61
-rw-r--r--tests/test-hmp.c7
-rw-r--r--tests/test-uuid.c8
-rw-r--r--tests/vhost-user-test.c1
-rw-r--r--tests/virtio-9p-test.c33
-rw-r--r--tests/vmgenid-test.c3
30 files changed, 1046 insertions, 146 deletions
diff --git a/tests/Makefile.include b/tests/Makefile.include
index c002352134..39a4b5359d 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -3,21 +3,21 @@
check-help:
@echo "Regression testing targets:"
@echo
- @echo " make check Run all tests"
- @echo " make check-qtest-TARGET Run qtest tests for given target"
- @echo " make check-qtest Run qtest tests"
- @echo " make check-unit Run qobject tests"
- @echo " make check-speed Run qobject speed tests"
- @echo " make check-qapi-schema Run QAPI schema tests"
- @echo " make check-block Run block tests"
- @echo " make check-report.html Generates an HTML test report"
- @echo " make check-clean Clean the tests"
+ @echo " $(MAKE) check Run all tests"
+ @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target"
+ @echo " $(MAKE) check-qtest Run qtest tests"
+ @echo " $(MAKE) check-unit Run qobject tests"
+ @echo " $(MAKE) check-speed Run qobject speed tests"
+ @echo " $(MAKE) check-qapi-schema Run QAPI schema tests"
+ @echo " $(MAKE) check-block Run block tests"
+ @echo " $(MAKE) check-report.html Generates an HTML test report"
+ @echo " $(MAKE) check-clean Clean the tests"
@echo
@echo "Please note that HTML reports do not regenerate if the unit tests"
@echo "has not changed."
@echo
@echo "The variable SPEED can be set to control the gtester speed setting."
- @echo "Default options are -k and (for make V=1) --verbose; they can be"
+ @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be"
@echo "changed with variable GTESTER_OPTIONS."
ifneq ($(wildcard config-host.mak),)
@@ -80,6 +80,7 @@ gcov-files-test-thread-pool-y = thread-pool.c
gcov-files-test-hbitmap-y = util/hbitmap.c
check-unit-y += tests/test-hbitmap$(EXESUF)
gcov-files-test-hbitmap-y = blockjob.c
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
check-unit-y += tests/test-blockjob$(EXESUF)
check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF)
@@ -297,6 +298,8 @@ gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)
check-qtest-alpha-y = tests/boot-serial-test$(EXESUF)
+check-qtest-m68k-y = tests/boot-serial-test$(EXESUF)
+
check-qtest-mips-y = tests/endianness-test$(EXESUF)
check-qtest-mips64-y = tests/endianness-test$(EXESUF)
@@ -416,6 +419,7 @@ qapi-schema += command-int.json
qapi-schema += comments.json
qapi-schema += doc-bad-alternate-member.json
qapi-schema += doc-bad-command-arg.json
+qapi-schema += doc-bad-section.json
qapi-schema += doc-bad-symbol.json
qapi-schema += doc-bad-union-member.json
qapi-schema += doc-before-include.json
@@ -433,10 +437,10 @@ qapi-schema += doc-invalid-end2.json
qapi-schema += doc-invalid-return.json
qapi-schema += doc-invalid-section.json
qapi-schema += doc-invalid-start.json
-qapi-schema += doc-missing.json
qapi-schema += doc-missing-colon.json
qapi-schema += doc-missing-expr.json
qapi-schema += doc-missing-space.json
+qapi-schema += doc-missing.json
qapi-schema += doc-no-symbol.json
qapi-schema += double-data.json
qapi-schema += double-type.json
@@ -592,6 +596,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c
index c935d69824..dd3828c49b 100644
--- a/tests/boot-serial-test.c
+++ b/tests/boot-serial-test.c
@@ -7,19 +7,31 @@
* or later. See the COPYING file in the top-level directory.
*
* This test is used to check that the serial output of the firmware
- * (that we provide for some machines) contains an expected string.
- * Thus we check that the firmware still boots at least to a certain
- * point and so we know that the machine is not completely broken.
+ * (that we provide for some machines) or some small mini-kernels that
+ * we provide here contains an expected string. Thus we check that the
+ * firmware/kernel still boots at least to a certain point and so we
+ * know that the machine is not completely broken.
*/
#include "qemu/osdep.h"
#include "libqtest.h"
+static const uint8_t kernel_mcf5208[] = {
+ 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
+ 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
+ 0x11, 0x7c, 0x00, 0x04, 0x00, 0x08, /* move.b #4,8(%a0) Enable TX */
+ 0x11, 0x40, 0x00, 0x0c, /* move.b %d0,12(%a0) Print 'T' */
+ 0x60, 0xfa /* bra.s loop */
+};
+
typedef struct testdef {
const char *arch; /* Target architecture */
const char *machine; /* Name of the machine */
const char *extra; /* Additional parameters */
const char *expect; /* Expected string in the serial output */
+ size_t codesize; /* Size of the kernel or bios data */
+ const uint8_t *kernel; /* Set in case we use our own mini kernel */
+ const uint8_t *bios; /* Set in case we use our own mini bios */
} testdef_t;
static testdef_t tests[] = {
@@ -37,18 +49,21 @@ static testdef_t tests[] = {
{ "x86_64", "q35", "-device sga", "SGABIOS" },
{ "s390x", "s390-ccw-virtio",
"-nodefaults -device sclpconsole,chardev=serial0", "virtio device" },
+ { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 },
+
{ NULL }
};
static void check_guest_output(const testdef_t *test, int fd)
{
bool output_ok = false;
- int i, nbr, pos = 0;
+ int i, nbr, pos = 0, ccnt;
char ch;
/* Poll serial output... Wait at most 60 seconds */
for (i = 0; i < 6000; ++i) {
- while ((nbr = read(fd, &ch, 1)) == 1) {
+ ccnt = 0;
+ while ((nbr = read(fd, &ch, 1)) == 1 && ccnt++ < 512) {
if (ch == test->expect[pos]) {
pos += 1;
if (test->expect[pos] == '\0') {
@@ -71,26 +86,52 @@ done:
static void test_machine(const void *data)
{
const testdef_t *test = data;
- char tmpname[] = "/tmp/qtest-boot-serial-XXXXXX";
- int fd;
+ char serialtmp[] = "/tmp/qtest-boot-serial-sXXXXXX";
+ char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX";
+ const char *codeparam = "";
+ const uint8_t *code = NULL;
+ int ser_fd;
- fd = mkstemp(tmpname);
- g_assert(fd != -1);
+ ser_fd = mkstemp(serialtmp);
+ g_assert(ser_fd != -1);
+
+ if (test->kernel) {
+ code = test->kernel;
+ codeparam = "-kernel";
+ } else if (test->bios) {
+ code = test->bios;
+ codeparam = "-bios";
+ }
+
+ if (code) {
+ ssize_t wlen;
+ int code_fd;
+
+ code_fd = mkstemp(codetmp);
+ g_assert(code_fd != -1);
+ wlen = write(code_fd, code, test->codesize);
+ g_assert(wlen == test->codesize);
+ close(code_fd);
+ }
/*
* Make sure that this test uses tcg if available: It is used as a
* fast-enough smoketest for that.
*/
- global_qtest = qtest_startf("-M %s,accel=tcg:kvm "
+ global_qtest = qtest_startf("%s %s -M %s,accel=tcg:kvm "
"-chardev file,id=serial0,path=%s "
"-no-shutdown -serial chardev:serial0 %s",
- test->machine, tmpname, test->extra);
- unlink(tmpname);
+ codeparam, code ? codetmp : "",
+ test->machine, serialtmp, test->extra);
+ unlink(serialtmp);
+ if (code) {
+ unlink(codetmp);
+ }
- check_guest_output(test, fd);
+ check_guest_output(test, ser_fd);
qtest_quit(global_qtest);
- close(fd);
+ close(ser_fd);
}
int main(int argc, char *argv[])
diff --git a/tests/docker/test-full b/tests/docker/test-full
index 816d5a3eec..b4e42d25d7 100755
--- a/tests/docker/test-full
+++ b/tests/docker/test-full
@@ -1,8 +1,8 @@
#!/bin/bash
#
-# Compile all the targets with as many features enabled as possible
+# Compile all the targets.
#
-# Copyright 2016, 2017 Red Hat Inc.
+# Copyright (c) 2016 Red Hat Inc.
#
# Authors:
# Fam Zheng <famz@redhat.com>
@@ -13,77 +13,6 @@
. common.rc
-cd "$BUILD_DIR" || exit 1
+cd "$BUILD_DIR"
-build_qemu \
- --enable-attr \
- --enable-bluez \
- --enable-brlapi \
- --enable-bsd-user \
- --enable-bzip2 \
- --enable-cap-ng \
- --enable-coroutine-pool \
- --enable-crypto-afalg \
- --enable-curl \
- --enable-curses \
- --enable-debug \
- --enable-debug-info \
- --enable-debug-tcg \
- --enable-docs \
- --enable-fdt \
- --enable-gcrypt \
- --enable-glusterfs \
- --enable-gnutls \
- --enable-gprof \
- --enable-gtk \
- --enable-guest-agent \
- --enable-jemalloc \
- --enable-kvm \
- --enable-libiscsi \
- --enable-libnfs \
- --enable-libssh2 \
- --enable-libusb \
- --enable-linux-aio \
- --enable-linux-user \
- --enable-live-block-migration \
- --enable-lzo \
- --enable-modules \
- --enable-numa \
- --enable-opengl \
- --enable-pie \
- --enable-profiler \
- --enable-qom-cast-debug \
- --enable-rbd \
- --enable-rdma \
- --enable-replication \
- --enable-sdl \
- --enable-seccomp \
- --enable-smartcard \
- --enable-snappy \
- --enable-spice \
- --enable-stack-protector \
- --enable-system \
- --enable-tcg \
- --enable-tcg-interpreter \
- --enable-tools \
- --enable-tpm \
- --enable-trace-backend=ftrace \
- --enable-usb-redir \
- --enable-user \
- --enable-vde \
- --enable-vhost-net \
- --enable-vhost-scsi \
- --enable-vhost-user \
- --enable-vhost-vsock \
- --enable-virtfs \
- --enable-vnc \
- --enable-vnc-jpeg \
- --enable-vnc-png \
- --enable-vnc-sasl \
- --enable-vte \
- --enable-werror \
- --enable-xen \
- --enable-xen-pci-passthrough \
- --enable-xen-pv-domain-build \
- --enable-xfsctl \
-&& make check $MAKEFLAGS && install_qemu
+build_qemu && make check $MAKEFLAGS && install_qemu
diff --git a/tests/qapi-schema/doc-bad-section.err b/tests/qapi-schema/doc-bad-section.err
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.err
diff --git a/tests/qapi-schema/doc-bad-section.exit b/tests/qapi-schema/doc-bad-section.exit
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/doc-bad-section.json b/tests/qapi-schema/doc-bad-section.json
new file mode 100644
index 0000000000..560df4b087
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.json
@@ -0,0 +1,11 @@
+# = section within an expression comment
+# BUG: not rejected
+
+##
+# @Enum:
+# == Produces *invalid* texinfo
+# @one: The _one_ {and only}
+#
+# @two is undocumented
+##
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out
new file mode 100644
index 0000000000..089bde1381
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-section.out
@@ -0,0 +1,13 @@
+enum Enum ['one', 'two']
+enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+ prefix QTYPE
+object q_empty
+doc symbol=Enum
+ body=
+== Produces *invalid* texinfo
+ arg=one
+The _one_ {and only}
+ arg=two
+
+ section=None
+@two is undocumented
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index cfdc0a8a81..97ab4625ff 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -51,7 +51,6 @@
##
# @Enum:
-# == Produces *invalid* texinfo
# @one: The _one_ {and only}
#
# @two is undocumented
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 63ca25a8b9..1d2c250527 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -77,12 +77,12 @@ Examples:
- {braces}
doc symbol=Enum
body=
-== Produces *invalid* texinfo
+
arg=one
The _one_ {and only}
arg=two
- section=
+ section=None
@two is undocumented
doc symbol=Base
body=
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index c410626e4a..1778312581 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -76,7 +76,7 @@ Examples:
@deftp {Enum} Enum
-@subsection Produces @strong{invalid} texinfo
+
@b{Values:}
@table @asis
@@ -101,7 +101,6 @@ Not documented
the first member
@end table
-
@end deftp
@@ -118,7 +117,6 @@ Another paragraph (but no @code{var}: line)
Not documented
@end table
-
@end deftp
@@ -127,7 +125,6 @@ Not documented
-
@end deftp
@@ -143,7 +140,6 @@ Not documented
@item The members of @code{Variant2} when @code{base1} is @t{"two"}
@end table
-
@end deftp
@@ -160,7 +156,6 @@ One of @t{"one"}, @t{"two"}
@item @code{data: Variant2} when @code{type} is @t{"two"}
@end table
-
@end deftp
@@ -182,7 +177,6 @@ argument
Not documented
@end table
-
@b{Note:}
@code{arg3} is undocumented
@@ -209,14 +203,12 @@ Duis aute irure dolor
<- out
@end example
-
@b{Examples:}
@example
- *verbatim*
- @{braces@}
@end example
-
@b{Since:}
2.10
@@ -237,7 +229,6 @@ If you're bored enough to read this, go see a video of boxed cats
<- out
@end example
-
@end deftypefn
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index c7724d3437..fe0ca08d78 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -61,8 +61,8 @@ for doc in schema.docs:
print 'doc symbol=%s' % doc.symbol
else:
print 'doc freeform'
- print ' body=\n%s' % doc.body
+ print ' body=\n%s' % doc.body.text
for arg, section in doc.args.iteritems():
- print ' arg=%s\n%s' % (arg, section)
+ print ' arg=%s\n%s' % (arg, section.text)
for section in doc.sections:
- print ' section=%s\n%s' % (section.name, section)
+ print ' section=%s\n%s' % (section.name, section.text)
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
index 887eb4f496..5e869fe2b7 100755
--- a/tests/qemu-iotests/197
+++ b/tests/qemu-iotests/197
@@ -60,6 +60,10 @@ echo '=== Copy-on-read ==='
echo
# Prep the images
+# VPC rounds image sizes to a specific geometry, force a specific size.
+if [ "$IMGFMT" = "vpc" ]; then
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
+fi
_make_test_img 4G
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202
new file mode 100755
index 0000000000..581ca34d79
--- /dev/null
+++ b/tests/qemu-iotests/202
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
+#
+# Check that QMP 'transaction' blockdev-snapshot-sync with multiple drives on a
+# single IOThread completes successfully. This particular command triggered a
+# hang due to recursive AioContext locking and BDRV_POLL_WHILE(). Protect
+# against regressions.
+
+import iotests
+
+iotests.verify_image_format(supported_fmts=['qcow2'])
+iotests.verify_platform(['linux'])
+
+with iotests.FilePath('disk0.img') as disk0_img_path, \
+ iotests.FilePath('disk1.img') as disk1_img_path, \
+ iotests.FilePath('disk0-snap.img') as disk0_snap_img_path, \
+ iotests.FilePath('disk1-snap.img') as disk1_snap_img_path, \
+ iotests.VM() as vm:
+
+ img_size = '10M'
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
+
+ iotests.log('Launching VM...')
+ vm.launch()
+
+ iotests.log('Adding IOThread...')
+ iotests.log(vm.qmp('object-add',
+ qom_type='iothread',
+ id='iothread0'))
+
+ iotests.log('Adding blockdevs...')
+ iotests.log(vm.qmp('blockdev-add',
+ driver=iotests.imgfmt,
+ node_name='disk0',
+ file={
+ 'driver': 'file',
+ 'filename': disk0_img_path,
+ }))
+ iotests.log(vm.qmp('blockdev-add',
+ driver=iotests.imgfmt,
+ node_name='disk1',
+ file={
+ 'driver': 'file',
+ 'filename': disk1_img_path,
+ }))
+
+ iotests.log('Setting iothread...')
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
+ node_name='disk0',
+ iothread='iothread0'))
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
+ node_name='disk1',
+ iothread='iothread0'))
+
+ iotests.log('Creating external snapshots...')
+ iotests.log(vm.qmp(
+ 'transaction',
+ actions=[
+ {
+ 'data': {
+ 'node-name': 'disk0',
+ 'snapshot-file': disk0_snap_img_path,
+ 'snapshot-node-name': 'disk0-snap',
+ 'mode': 'absolute-paths',
+ 'format': iotests.imgfmt,
+ },
+ 'type': 'blockdev-snapshot-sync'
+ }, {
+ 'data': {
+ 'node-name': 'disk1',
+ 'snapshot-file': disk1_snap_img_path,
+ 'snapshot-node-name': 'disk1-snap',
+ 'mode': 'absolute-paths',
+ 'format': iotests.imgfmt
+ },
+ 'type': 'blockdev-snapshot-sync'
+ }
+ ]))
diff --git a/tests/qemu-iotests/202.out b/tests/qemu-iotests/202.out
new file mode 100644
index 0000000000..d5ea374e17
--- /dev/null
+++ b/tests/qemu-iotests/202.out
@@ -0,0 +1,11 @@
+Launching VM...
+Adding IOThread...
+{u'return': {}}
+Adding blockdevs...
+{u'return': {}}
+{u'return': {}}
+Setting iothread...
+{u'return': {}}
+{u'return': {}}
+Creating external snapshots...
+{u'return': {}}
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
new file mode 100755
index 0000000000..2c811917d8
--- /dev/null
+++ b/tests/qemu-iotests/203
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
+#
+# Check that QMP 'migrate' with multiple drives on a single IOThread completes
+# successfully. This particular command triggered a hang in the source QEMU
+# process due to recursive AioContext locking in bdrv_invalidate_all() and
+# BDRV_POLL_WHILE().
+
+import iotests
+
+iotests.verify_image_format(supported_fmts=['qcow2'])
+iotests.verify_platform(['linux'])
+
+with iotests.FilePath('disk0.img') as disk0_img_path, \
+ iotests.FilePath('disk1.img') as disk1_img_path, \
+ iotests.VM() as vm:
+
+ img_size = '10M'
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
+
+ iotests.log('Launching VM...')
+ (vm.add_object('iothread,id=iothread0')
+ .add_drive(disk0_img_path, 'node-name=drive0-node', interface='none')
+ .add_drive(disk1_img_path, 'node-name=drive1-node', interface='none')
+ .launch())
+
+ iotests.log('Setting IOThreads...')
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
+ node_name='drive0-node', iothread='iothread0',
+ force=True))
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
+ node_name='drive1-node', iothread='iothread0',
+ force=True))
+
+ iotests.log('Starting migration...')
+ iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
+ while True:
+ vm.get_qmp_event(wait=60.0)
+ result = vm.qmp('query-migrate')
+ status = result.get('return', {}).get('status', None)
+ if status == 'completed':
+ break
diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out
new file mode 100644
index 0000000000..3f1ff900e4
--- /dev/null
+++ b/tests/qemu-iotests/203.out
@@ -0,0 +1,6 @@
+Launching VM...
+Setting IOThreads...
+{u'return': {}}
+{u'return': {}}
+Starting migration...
+{u'return': {}}
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index d9237799e9..f08248bfd9 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -134,7 +134,8 @@ _filter_img_create()
-e "s# log_size=[0-9]\\+##g" \
-e "s# refcount_bits=[0-9]\\+##g" \
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
- -e "s# iter-time=[0-9]\\+##g"
+ -e "s# iter-time=[0-9]\\+##g" \
+ -e "s# force_size=\\(on\\|off\\)##g"
}
_filter_img_info()
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 3e688678dd..93d96fb22f 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -197,3 +197,5 @@
197 rw auto quick
198 rw auto
200 rw auto
+202 rw auto quick
+203 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 6f057904a9..44477e9295 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -197,6 +197,11 @@ class VM(qtest.QEMUQtestMachine):
socket_scm_helper=socket_scm_helper)
self._num_drives = 0
+ def add_object(self, opts):
+ self._args.append('-object')
+ self._args.append(opts)
+ return self
+
def add_device(self, opts):
self._args.append('-device')
self._args.append(opts)
diff --git a/tests/tcg/xtensa/test_sr.S b/tests/tcg/xtensa/test_sr.S
index 42e3e5e386..052f1e04a7 100644
--- a/tests/tcg/xtensa/test_sr.S
+++ b/tests/tcg/xtensa/test_sr.S
@@ -44,7 +44,6 @@ test_end
test_sr acchi, 1
test_sr acclo, 1
-test_sr /*memctl*/97, 0
test_sr_mask /*atomctl*/99, 0, 0
test_sr_mask /*br*/4, 0, 0
test_sr_mask /*cacheattr*/98, 0, 0
@@ -76,6 +75,7 @@ test_sr lcount, 1
test_sr lend, 1
test_sr litbase, 1
test_sr m0, 1
+test_sr_mask /*memctl*/97, 0, 0
test_sr misc0, 1
test_sr_mask /*prefctl*/40, 0, 0
test_sr_mask /*prid*/235, 0, 1
diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c
index d396185972..c8bec81520 100644
--- a/tests/test-aio-multithread.c
+++ b/tests/test-aio-multithread.c
@@ -11,7 +11,6 @@
*/
#include "qemu/osdep.h"
-#include <glib.h>
#include "block/aio.h"
#include "qapi/error.h"
#include "qemu/coroutine.h"
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
new file mode 100644
index 0000000000..d760e2b243
--- /dev/null
+++ b/tests/test-bdrv-drain.c
@@ -0,0 +1,651 @@
+/*
+ * Block node draining tests
+ *
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block.h"
+#include "block/blockjob_int.h"
+#include "sysemu/block-backend.h"
+#include "qapi/error.h"
+
+typedef struct BDRVTestState {
+ int drain_count;
+} BDRVTestState;
+
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ s->drain_count++;
+}
+
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ s->drain_count--;
+}
+
+static void bdrv_test_close(BlockDriverState *bs)
+{
+ BDRVTestState *s = bs->opaque;
+ g_assert_cmpint(s->drain_count, >, 0);
+}
+
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ /* We want this request to stay until the polling loop in drain waits for
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
+ * first and polls its result, too, but it shouldn't accidentally complete
+ * this request yet. */
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
+
+ return 0;
+}
+
+static BlockDriver bdrv_test = {
+ .format_name = "test",
+ .instance_size = sizeof(BDRVTestState),
+
+ .bdrv_close = bdrv_test_close,
+ .bdrv_co_preadv = bdrv_test_co_preadv,
+
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
+
+ .bdrv_child_perm = bdrv_format_default_perms,
+};
+
+static void aio_ret_cb(void *opaque, int ret)
+{
+ int *aio_ret = opaque;
+ *aio_ret = ret;
+}
+
+typedef struct CallInCoroutineData {
+ void (*entry)(void);
+ bool done;
+} CallInCoroutineData;
+
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
+{
+ CallInCoroutineData *data = opaque;
+
+ data->entry();
+ data->done = true;
+}
+
+static void call_in_coroutine(void (*entry)(void))
+{
+ Coroutine *co;
+ CallInCoroutineData data = {
+ .entry = entry,
+ .done = false,
+ };
+
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
+ qemu_coroutine_enter(co);
+ while (!data.done) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+}
+
+enum drain_type {
+ BDRV_DRAIN_ALL,
+ BDRV_DRAIN,
+ BDRV_SUBTREE_DRAIN,
+ DRAIN_TYPE_MAX,
+};
+
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
+{
+ switch (drain_type) {
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
+ default: g_assert_not_reached();
+ }
+}
+
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
+{
+ switch (drain_type) {
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
+ default: g_assert_not_reached();
+ }
+}
+
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+ BDRVTestState *s, *backing_s;
+ BlockAIOCB *acb;
+ int aio_ret;
+
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = NULL,
+ .iov_len = 0,
+ };
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ s = bs->opaque;
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ /* Now do the same while a request is pending */
+ aio_ret = -EINPROGRESS;
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
+ g_assert(acb != NULL);
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(aio_ret, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_drv_cb_drain_all(void)
+{
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
+}
+
+static void test_drv_cb_drain(void)
+{
+ test_drv_cb_common(BDRV_DRAIN, false);
+}
+
+static void test_drv_cb_drain_subtree(void)
+{
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
+}
+
+static void test_drv_cb_co_drain(void)
+{
+ call_in_coroutine(test_drv_cb_drain);
+}
+
+static void test_drv_cb_co_drain_subtree(void)
+{
+ call_in_coroutine(test_drv_cb_drain_subtree);
+}
+
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+
+ do_drain_begin(drain_type, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
+
+ do_drain_end(drain_type, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_quiesce_drain_all(void)
+{
+ // XXX drain_all doesn't quiesce
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
+}
+
+static void test_quiesce_drain(void)
+{
+ test_quiesce_common(BDRV_DRAIN, false);
+}
+
+static void test_quiesce_drain_subtree(void)
+{
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
+}
+
+static void test_quiesce_co_drain(void)
+{
+ call_in_coroutine(test_quiesce_drain);
+}
+
+static void test_quiesce_co_drain_subtree(void)
+{
+ call_in_coroutine(test_quiesce_drain_subtree);
+}
+
+static void test_nested(void)
+{
+ BlockBackend *blk;
+ BlockDriverState *bs, *backing;
+ BDRVTestState *s, *backing_s;
+ enum drain_type outer, inner;
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+ s = bs->opaque;
+ blk_insert_bs(blk, bs, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs, backing, &error_abort);
+
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
+ (inner != BDRV_DRAIN_ALL);
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
+ (inner == BDRV_SUBTREE_DRAIN);
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
+ (inner != BDRV_DRAIN);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(outer, bs);
+ do_drain_begin(inner, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
+ g_assert_cmpint(s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
+
+ do_drain_end(inner, bs);
+ do_drain_end(outer, bs);
+
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+ }
+ }
+
+ bdrv_unref(backing);
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+static void test_multiparent(void)
+{
+ BlockBackend *blk_a, *blk_b;
+ BlockDriverState *bs_a, *bs_b, *backing;
+ BDRVTestState *a_s, *b_s, *backing_s;
+
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
+ &error_abort);
+ a_s = bs_a->opaque;
+ blk_insert_bs(blk_a, bs_a, &error_abort);
+
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
+ &error_abort);
+ b_s = bs_b->opaque;
+ blk_insert_bs(blk_b, bs_b, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
+ g_assert_cmpint(a_s->drain_count, ==, 1);
+ g_assert_cmpint(b_s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
+ g_assert_cmpint(a_s->drain_count, ==, 2);
+ g_assert_cmpint(b_s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
+ g_assert_cmpint(a_s->drain_count, ==, 1);
+ g_assert_cmpint(b_s->drain_count, ==, 1);
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs_a);
+ bdrv_unref(bs_b);
+ blk_unref(blk_a);
+ blk_unref(blk_b);
+}
+
+static void test_graph_change(void)
+{
+ BlockBackend *blk_a, *blk_b;
+ BlockDriverState *bs_a, *bs_b, *backing;
+ BDRVTestState *a_s, *b_s, *backing_s;
+
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
+ &error_abort);
+ a_s = bs_a->opaque;
+ blk_insert_bs(blk_a, bs_a, &error_abort);
+
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
+ &error_abort);
+ b_s = bs_b->opaque;
+ blk_insert_bs(blk_b, bs_b, &error_abort);
+
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
+ backing_s = backing->opaque;
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
+
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
+ g_assert_cmpint(a_s->drain_count, ==, 5);
+ g_assert_cmpint(b_s->drain_count, ==, 5);
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
+
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
+ g_assert_cmpint(a_s->drain_count, ==, 3);
+ g_assert_cmpint(b_s->drain_count, ==, 2);
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
+
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
+ g_assert_cmpint(a_s->drain_count, ==, 5);
+ g_assert_cmpint(b_s->drain_count, ==, 5);
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
+
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
+
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
+ g_assert_cmpint(a_s->drain_count, ==, 0);
+ g_assert_cmpint(b_s->drain_count, ==, 0);
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
+
+ bdrv_unref(backing);
+ bdrv_unref(bs_a);
+ bdrv_unref(bs_b);
+ blk_unref(blk_a);
+ blk_unref(blk_b);
+}
+
+
+typedef struct TestBlockJob {
+ BlockJob common;
+ bool should_complete;
+} TestBlockJob;
+
+static void test_job_completed(BlockJob *job, void *opaque)
+{
+ block_job_completed(job, 0);
+}
+
+static void coroutine_fn test_job_start(void *opaque)
+{
+ TestBlockJob *s = opaque;
+
+ while (!s->should_complete) {
+ block_job_sleep_ns(&s->common, 100000);
+ }
+
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
+}
+
+static void test_job_complete(BlockJob *job, Error **errp)
+{
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
+ s->should_complete = true;
+}
+
+BlockJobDriver test_job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+ .start = test_job_start,
+ .complete = test_job_complete,
+};
+
+static void test_blockjob_common(enum drain_type drain_type)
+{
+ BlockBackend *blk_src, *blk_target;
+ BlockDriverState *src, *target;
+ BlockJob *job;
+ int ret;
+
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
+ &error_abort);
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ blk_insert_bs(blk_src, src, &error_abort);
+
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
+ &error_abort);
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ blk_insert_bs(blk_target, target, &error_abort);
+
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
+ 0, NULL, NULL, &error_abort);
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
+ block_job_start(job);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ do_drain_begin(drain_type, src);
+
+ if (drain_type == BDRV_DRAIN_ALL) {
+ /* bdrv_drain_all() drains both src and target */
+ g_assert_cmpint(job->pause_count, ==, 2);
+ } else {
+ g_assert_cmpint(job->pause_count, ==, 1);
+ }
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
+ /* g_assert_true(job->paused); */
+ g_assert_false(job->busy); /* The job is paused */
+
+ do_drain_end(drain_type, src);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ do_drain_begin(drain_type, target);
+
+ if (drain_type == BDRV_DRAIN_ALL) {
+ /* bdrv_drain_all() drains both src and target */
+ g_assert_cmpint(job->pause_count, ==, 2);
+ } else {
+ g_assert_cmpint(job->pause_count, ==, 1);
+ }
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
+ /* g_assert_true(job->paused); */
+ g_assert_false(job->busy); /* The job is paused */
+
+ do_drain_end(drain_type, target);
+
+ g_assert_cmpint(job->pause_count, ==, 0);
+ g_assert_false(job->paused);
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
+
+ ret = block_job_complete_sync(job, &error_abort);
+ g_assert_cmpint(ret, ==, 0);
+
+ blk_unref(blk_src);
+ blk_unref(blk_target);
+ bdrv_unref(src);
+ bdrv_unref(target);
+}
+
+static void test_blockjob_drain_all(void)
+{
+ test_blockjob_common(BDRV_DRAIN_ALL);
+}
+
+static void test_blockjob_drain(void)
+{
+ test_blockjob_common(BDRV_DRAIN);
+}
+
+static void test_blockjob_drain_subtree(void)
+{
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
+}
+
+int main(int argc, char **argv)
+{
+ bdrv_init();
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
+ test_drv_cb_drain_subtree);
+
+ // XXX bdrv_drain_all() doesn't work in coroutine context
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
+ test_drv_cb_co_drain_subtree);
+
+
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
+ test_quiesce_drain_subtree);
+
+ // XXX bdrv_drain_all() doesn't work in coroutine context
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
+ test_quiesce_co_drain_subtree);
+
+ g_test_add_func("/bdrv-drain/nested", test_nested);
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
+
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
+ test_blockjob_drain_subtree);
+
+ return g_test_run();
+}
diff --git a/tests/test-char.c b/tests/test-char.c
index 7ac25ff73f..911e3f6e8d 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -5,6 +5,7 @@
#include "qemu/config-file.h"
#include "qemu/sockets.h"
#include "chardev/char-fe.h"
+#include "chardev/char-mux.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
#include "qom/qom-qobject.h"
@@ -164,6 +165,7 @@ static void char_mux_test(void)
FeHandler h1 = { 0, }, h2 = { 0, };
CharBackend chr_be1, chr_be2;
+ muxes_realized = true; /* done after machine init */
opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
1, &error_abort);
qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
@@ -201,8 +203,23 @@ static void char_mux_test(void)
g_assert_cmpstr(h2.read_buf, ==, "hello");
h2.read_count = 0;
+ g_assert_cmpint(h1.last_event, !=, 42); /* should be MUX_OUT or OPENED */
+ g_assert_cmpint(h2.last_event, !=, 42); /* should be MUX_IN or OPENED */
+ /* sending event on the base broadcast to all fe, historical reasons? */
+ qemu_chr_be_event(base, 42);
+ g_assert_cmpint(h1.last_event, ==, 42);
+ g_assert_cmpint(h2.last_event, ==, 42);
+ qemu_chr_be_event(chr, -1);
+ g_assert_cmpint(h1.last_event, ==, 42);
+ g_assert_cmpint(h2.last_event, ==, -1);
+
/* switch focus */
qemu_chr_be_write(base, (void *)"\1c", 2);
+ g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN);
+ g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);
+ qemu_chr_be_event(chr, -1);
+ g_assert_cmpint(h1.last_event, ==, -1);
+ g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);
qemu_chr_be_write(base, (void *)"hello", 6);
g_assert_cmpint(h2.read_count, ==, 0);
diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c
index 96982163e4..ac6afc562e 100644
--- a/tests/test-clone-visitor.c
+++ b/tests/test-clone-visitor.c
@@ -8,7 +8,6 @@
*/
#include "qemu/osdep.h"
-#include <glib.h>
#include "qemu-common.h"
#include "qapi/clone-visitor.h"
diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c
index af41642346..9091c639b3 100644
--- a/tests/test-hbitmap.c
+++ b/tests/test-hbitmap.c
@@ -925,6 +925,61 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data,
hbitmap_iter_next(&hbi);
}
+static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start)
+{
+ int64_t ret1 = hbitmap_next_zero(data->hb, start);
+ int64_t ret2 = start;
+ for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) {
+ ;
+ }
+ if (ret2 == data->size) {
+ ret2 = -1;
+ }
+
+ g_assert_cmpint(ret1, ==, ret2);
+}
+
+static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity)
+{
+ hbitmap_test_init(data, L3, granularity);
+ test_hbitmap_next_zero_check(data, 0);
+ test_hbitmap_next_zero_check(data, L3 - 1);
+
+ hbitmap_set(data->hb, L2, 1);
+ test_hbitmap_next_zero_check(data, 0);
+ test_hbitmap_next_zero_check(data, L2 - 1);
+ test_hbitmap_next_zero_check(data, L2);
+ test_hbitmap_next_zero_check(data, L2 + 1);
+
+ hbitmap_set(data->hb, L2 + 5, L1);
+ test_hbitmap_next_zero_check(data, 0);
+ test_hbitmap_next_zero_check(data, L2 + 1);
+ test_hbitmap_next_zero_check(data, L2 + 2);
+ test_hbitmap_next_zero_check(data, L2 + 5);
+ test_hbitmap_next_zero_check(data, L2 + L1 - 1);
+ test_hbitmap_next_zero_check(data, L2 + L1);
+
+ hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2);
+ test_hbitmap_next_zero_check(data, L2 * 2 - L1);
+ test_hbitmap_next_zero_check(data, L2 * 2 - 2);
+ test_hbitmap_next_zero_check(data, L2 * 2 - 1);
+ test_hbitmap_next_zero_check(data, L2 * 2);
+ test_hbitmap_next_zero_check(data, L3 - 1);
+
+ hbitmap_set(data->hb, 0, L3);
+ test_hbitmap_next_zero_check(data, 0);
+}
+
+static void test_hbitmap_next_zero_0(TestHBitmapData *data, const void *unused)
+{
+ test_hbitmap_next_zero_do(data, 0);
+}
+
+static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused)
+{
+ test_hbitmap_next_zero_do(data, 4);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -985,6 +1040,12 @@ int main(int argc, char **argv)
hbitmap_test_add("/hbitmap/iter/iter_and_reset",
test_hbitmap_iter_and_reset);
+
+ hbitmap_test_add("/hbitmap/next_zero/next_zero_0",
+ test_hbitmap_next_zero_0);
+ hbitmap_test_add("/hbitmap/next_zero/next_zero_4",
+ test_hbitmap_next_zero_4);
+
g_test_run();
return 0;
diff --git a/tests/test-hmp.c b/tests/test-hmp.c
index 5677fbf775..5b7e447b6a 100644
--- a/tests/test-hmp.c
+++ b/tests/test-hmp.c
@@ -78,10 +78,13 @@ static void test_commands(void)
int i;
for (i = 0; hmp_cmds[i] != NULL; i++) {
+ response = hmp("%s", hmp_cmds[i]);
if (verbose) {
- fprintf(stderr, "\t%s\n", hmp_cmds[i]);
+ fprintf(stderr,
+ "\texecute HMP command: %s\n"
+ "\tresult : %s\n",
+ hmp_cmds[i], response);
}
- response = hmp("%s", hmp_cmds[i]);
g_free(response);
}
diff --git a/tests/test-uuid.c b/tests/test-uuid.c
index d3a2791fd4..22b4b0727d 100644
--- a/tests/test-uuid.c
+++ b/tests/test-uuid.c
@@ -93,12 +93,18 @@ static inline bool uuid_is_valid(QemuUUID *uuid)
static void test_uuid_generate(void)
{
+ QemuUUID uuid_not_null = { { {
+ 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0,
+ 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42
+ } } };
QemuUUID uuid;
int i;
for (i = 0; i < 100; ++i) {
qemu_uuid_generate(&uuid);
g_assert(uuid_is_valid(&uuid));
+ g_assert_false(qemu_uuid_is_null(&uuid));
+ g_assert_false(qemu_uuid_is_equal(&uuid_not_null, &uuid));
}
}
@@ -168,8 +174,8 @@ static void test_uuid_unparse_strdup(void)
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
- g_test_add_func("/uuid/generate", test_uuid_generate);
g_test_add_func("/uuid/is_null", test_uuid_is_null);
+ g_test_add_func("/uuid/generate", test_uuid_generate);
g_test_add_func("/uuid/parse", test_uuid_parse);
g_test_add_func("/uuid/unparse", test_uuid_unparse);
g_test_add_func("/uuid/unparse_strdup", test_uuid_unparse_strdup);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 4b98018478..e2c89ed376 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -21,7 +21,6 @@
#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
-#include "qapi/error.h"
#include "libqos/malloc-pc.h"
#include "hw/virtio/virtio-net.h"
diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c
index ad33d96387..00f00f7246 100644
--- a/tests/virtio-9p-test.c
+++ b/tests/virtio-9p-test.c
@@ -18,6 +18,8 @@
#include "standard-headers/linux/virtio_pci.h"
#include "hw/9pfs/9p.h"
+#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
+
static const char mount_tag[] = "qtest";
typedef struct {
@@ -73,6 +75,9 @@ static QVirtIO9P *qvirtio_9p_pci_start(void)
qvirtio_set_driver(v9p->dev);
v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0);
+
+ qvirtio_set_driver_ok(v9p->dev);
+
return v9p;
}
@@ -111,6 +116,7 @@ typedef struct {
/* No r_size, it is hardcoded to P9_MAX_SIZE */
size_t t_off;
size_t r_off;
+ uint32_t free_head;
} P9Req;
static void v9fs_memwrite(P9Req *req, const void *addr, size_t len)
@@ -124,11 +130,6 @@ static void v9fs_memskip(P9Req *req, size_t len)
req->r_off += len;
}
-static void v9fs_memrewind(P9Req *req, size_t len)
-{
- req->r_off -= len;
-}
-
static void v9fs_memread(P9Req *req, void *addr, size_t len)
{
memread(req->r_msg + req->r_off, addr, len);
@@ -227,12 +228,12 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id,
static void v9fs_req_send(P9Req *req)
{
QVirtIO9P *v9p = req->v9p;
- uint32_t free_head;
req->r_msg = guest_alloc(v9p->qs->alloc, P9_MAX_SIZE);
- free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false, true);
+ req->free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false,
+ true);
qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
- qvirtqueue_kick(v9p->dev, v9p->vq, free_head);
+ qvirtqueue_kick(v9p->dev, v9p->vq, req->free_head);
req->t_off = 0;
}
@@ -250,19 +251,13 @@ static void v9fs_req_recv(P9Req *req, uint8_t id)
{
QVirtIO9P *v9p = req->v9p;
P9Hdr hdr;
- int i;
- for (i = 0; i < 10; i++) {
- qvirtio_wait_queue_isr(v9p->dev, v9p->vq, 1000 * 1000);
+ qvirtio_wait_used_elem(v9p->dev, v9p->vq, req->free_head,
+ QVIRTIO_9P_TIMEOUT_US);
- v9fs_memread(req, &hdr, 7);
- hdr.size = ldl_le_p(&hdr.size);
- hdr.tag = lduw_le_p(&hdr.tag);
- if (hdr.size >= 7) {
- break;
- }
- v9fs_memrewind(req, 7);
- }
+ v9fs_memread(req, &hdr, 7);
+ hdr.size = ldl_le_p(&hdr.size);
+ hdr.tag = lduw_le_p(&hdr.tag);
g_assert_cmpint(hdr.size, >=, 7);
g_assert_cmpint(hdr.size, <=, P9_MAX_SIZE);
diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
index 5a86b40775..68ff954578 100644
--- a/tests/vmgenid-test.c
+++ b/tests/vmgenid-test.c
@@ -8,9 +8,6 @@
* See the COPYING file in the top-level directory.
*/
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
#include "qemu/osdep.h"
#include "qemu/bitmap.h"
#include "qemu/uuid.h"