aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rwxr-xr-xtests/check-block.sh12
-rwxr-xr-xtests/qemu-iotests/185190
-rw-r--r--tests/qemu-iotests/185.out48
-rwxr-xr-xtests/qemu-iotests/2712
-rwxr-xr-xtests/qemu-iotests/2968
-rw-r--r--tests/qemu-iotests/296.out17
-rw-r--r--tests/qemu-iotests/common.filter65
-rw-r--r--tests/qemu-iotests/common.rc45
-rw-r--r--tests/qemu-iotests/iotests.py32
-rwxr-xr-xtests/qemu-iotests/tests/graph-changes-while-io91
-rw-r--r--tests/qemu-iotests/tests/graph-changes-while-io.out5
-rw-r--r--tests/unit/rcutorture.c10
-rw-r--r--tests/unit/test-block-iothread.c8
-rw-r--r--tests/unit/test-rcu-list.c4
14 files changed, 443 insertions, 94 deletions
diff --git a/tests/check-block.sh b/tests/check-block.sh
index 18f7433901..f59496396c 100755
--- a/tests/check-block.sh
+++ b/tests/check-block.sh
@@ -48,18 +48,6 @@ if LANG=C bash --version | grep -q 'GNU bash, version [123]' ; then
skip "bash version too old ==> Not running the qemu-iotests."
fi
-if ! (sed --version | grep 'GNU sed') > /dev/null 2>&1 ; then
- if ! command -v gsed >/dev/null 2>&1; then
- skip "GNU sed not available ==> Not running the qemu-iotests."
- fi
-else
- # Double-check that we're not using BusyBox' sed which says
- # that "This is not GNU sed version 4.0" ...
- if sed --version | grep -q 'not GNU sed' ; then
- skip "BusyBox sed not supported ==> Not running the qemu-iotests."
- fi
-fi
-
cd tests/qemu-iotests
# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
index f2ec5c5ceb..8b1143dc16 100755
--- a/tests/qemu-iotests/185
+++ b/tests/qemu-iotests/185
@@ -33,6 +33,12 @@ _cleanup()
_rm_test_img "${TEST_IMG}.copy"
_cleanup_test_img
_cleanup_qemu
+
+ if [ -f "$TEST_DIR/qsd.pid" ]; then
+ kill -SIGKILL "$(cat "$TEST_DIR/qsd.pid")"
+ rm -f "$TEST_DIR/qsd.pid"
+ fi
+ rm -f "$SOCK_DIR/qsd.sock"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -45,7 +51,7 @@ _supported_fmt qcow2
_supported_proto file
_supported_os Linux
-size=64M
+size=$((64 * 1048576))
TEST_IMG="${TEST_IMG}.base" _make_test_img $size
echo
@@ -216,6 +222,188 @@ wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
_check_test_img
+echo
+echo === Start mirror to throttled QSD and exit qemu ===
+echo
+
+# Mirror to a throttled QSD instance (so that qemu cannot drain the
+# throttling), wait for READY, then write some data to the device,
+# and then quit qemu.
+# (qemu should force-cancel the job and not wait for the data to be
+# written to the target.)
+
+_make_test_img $size
+
+# Will be used by this and the next case
+set_up_throttled_qsd() {
+ $QSD \
+ --object throttle-group,id=thrgr,limits.bps-total=1048576 \
+ --blockdev null-co,node-name=null,size=$size \
+ --blockdev throttle,node-name=throttled,throttle-group=thrgr,file=null \
+ --nbd-server addr.type=unix,addr.path="$SOCK_DIR/qsd.sock" \
+ --export nbd,id=exp,node-name=throttled,name=target,writable=true \
+ --pidfile "$TEST_DIR/qsd.pid" \
+ --daemonize
+}
+
+set_up_throttled_qsd
+
+# Need a virtio-blk device so that qemu-io writes will not block the monitor
+_launch_qemu \
+ --blockdev file,node-name=source-proto,filename="$TEST_IMG" \
+ --blockdev qcow2,node-name=source-fmt,file=source-proto \
+ --device virtio-blk,id=vblk,drive=source-fmt \
+ --blockdev "{\"driver\": \"nbd\",
+ \"node-name\": \"target\",
+ \"server\": {
+ \"type\": \"unix\",
+ \"path\": \"$SOCK_DIR/qsd.sock\"
+ },
+ \"export\": \"target\"}"
+
+h=$QEMU_HANDLE
+_send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return'
+
+# Use sync=top, so the first pass will not copy the whole image
+_send_qemu_cmd $h \
+ '{"execute": "blockdev-mirror",
+ "arguments": {
+ "job-id": "mirror",
+ "device": "source-fmt",
+ "target": "target",
+ "sync": "top"
+ }}' \
+ 'return' \
+ | grep -v JOB_STATUS_CHANGE # Ignore these events during creation
+
+# This too will be used by this and the next case
+# $1: QEMU handle
+# $2: Image size
+wait_for_job_and_quit() {
+ h=$1
+ size=$2
+
+ # List of expected events
+ capture_events='BLOCK_JOB_READY JOB_STATUS_CHANGE'
+ _wait_event $h 'BLOCK_JOB_READY'
+ QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before READY
+
+ # Write something to the device for post-READY mirroring. Write it in
+ # blocks matching the cluster size, each spaced one block apart, so
+ # that the mirror job will have to spawn one request per cluster.
+ # Because the number of concurrent requests is limited (to 16), this
+ # limits the number of bytes concurrently in flight, which speeds up
+ # cancelling the job (in-flight requests still are waited for).
+ # To limit the number of bytes in flight, we could alternatively pass
+ # something for blockdev-mirror's @buf-size parameter, but
+ # block-commit does not have such a parameter, so we need to figure
+ # something out that works for both.
+
+ cluster_size=65536
+ step=$((cluster_size * 2))
+
+ echo '--- Writing data to the virtio-blk device ---'
+
+ for ofs in $(seq 0 $step $((size - step))); do
+ qemu_io_cmd="qemu-io -d vblk/virtio-backend "
+ qemu_io_cmd+="\\\"aio_write $ofs $cluster_size\\\""
+
+ # Do not include these requests in the reference output
+ # (it's just too much)
+ silent=yes _send_qemu_cmd $h \
+ "{\"execute\": \"human-monitor-command\",
+ \"arguments\": {
+ \"command-line\": \"$qemu_io_cmd\"
+ }}" \
+ 'return'
+ done
+
+ # Wait until the job's length is updated to reflect the write requests
+
+ # We have written to half of the device, so this is the expected job length
+ final_len=$((size / 2))
+ timeout=100 # unit: 0.1 seconds
+ while true; do
+ len=$(
+ _send_qemu_cmd $h \
+ '{"execute": "query-block-jobs"}' \
+ 'return.*"len": [0-9]\+' \
+ | grep 'return.*"len": [0-9]\+' \
+ | sed -e 's/.*"len": \([0-9]\+\).*/\1/'
+ )
+ if [ "$len" -eq "$final_len" ]; then
+ break
+ fi
+ timeout=$((timeout - 1))
+ if [ "$timeout" -eq 0 ]; then
+ echo "ERROR: Timeout waiting for job to reach len=$final_len"
+ break
+ fi
+ sleep 0.1
+ done
+
+ sleep 1
+
+ _send_qemu_cmd $h \
+ '{"execute": "quit"}' \
+ 'return'
+
+ # List of expected events
+ capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN'
+ _wait_event $h 'SHUTDOWN'
+ QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN
+ _wait_event $h 'JOB_STATUS_CHANGE' # standby
+ _wait_event $h 'JOB_STATUS_CHANGE' # ready
+ _wait_event $h 'JOB_STATUS_CHANGE' # aborting
+ # Filter the offset (depends on when exactly `quit` was issued)
+ _wait_event $h 'BLOCK_JOB_CANCELLED' \
+ | sed -e 's/"offset": [0-9]\+/"offset": (filtered)/'
+ _wait_event $h 'JOB_STATUS_CHANGE' # concluded
+ _wait_event $h 'JOB_STATUS_CHANGE' # null
+
+ wait=yes _cleanup_qemu
+
+ kill -SIGTERM "$(cat "$TEST_DIR/qsd.pid")"
+}
+
+wait_for_job_and_quit $h $size
+
+echo
+echo === Start active commit to throttled QSD and exit qemu ===
+echo
+
+# Same as the above, but instead of mirroring, do an active commit
+
+_make_test_img $size
+
+set_up_throttled_qsd
+
+_launch_qemu \
+ --blockdev "{\"driver\": \"nbd\",
+ \"node-name\": \"target\",
+ \"server\": {
+ \"type\": \"unix\",
+ \"path\": \"$SOCK_DIR/qsd.sock\"
+ },
+ \"export\": \"target\"}" \
+ --blockdev file,node-name=source-proto,filename="$TEST_IMG" \
+ --blockdev qcow2,node-name=source-fmt,file=source-proto,backing=target \
+ --device virtio-blk,id=vblk,drive=source-fmt
+
+h=$QEMU_HANDLE
+_send_qemu_cmd $h '{"execute": "qmp_capabilities"}' 'return'
+
+_send_qemu_cmd $h \
+ '{"execute": "block-commit",
+ "arguments": {
+ "job-id": "commit",
+ "device": "source-fmt"
+ }}' \
+ 'return' \
+ | grep -v JOB_STATUS_CHANGE # Ignore these events during creation
+
+wait_for_job_and_quit $h $size
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
index 754a641258..70e8dd6c87 100644
--- a/tests/qemu-iotests/185.out
+++ b/tests/qemu-iotests/185.out
@@ -116,4 +116,52 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}}
No errors were found on the image.
+
+=== Start mirror to throttled QSD and exit qemu ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+{"execute": "qmp_capabilities"}
+{"return": {}}
+{"execute": "blockdev-mirror",
+ "arguments": {
+ "job-id": "mirror",
+ "device": "source-fmt",
+ "target": "target",
+ "sync": "top"
+ }}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
+--- Writing data to the virtio-blk device ---
+{"execute": "quit"}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "mirror"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "mirror"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "mirror", "len": 33554432, "offset": (filtered), "speed": 0, "type": "mirror"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}}
+
+=== Start active commit to throttled QSD and exit qemu ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+{"execute": "qmp_capabilities"}
+{"return": {}}
+{"execute": "block-commit",
+ "arguments": {
+ "job-id": "commit",
+ "device": "source-fmt"
+ }}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "commit", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
+--- Writing data to the virtio-blk device ---
+{"execute": "quit"}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "commit", "len": 33554432, "offset": (filtered), "speed": 0, "type": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "commit"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "commit"}}
*** done
diff --git a/tests/qemu-iotests/271 b/tests/qemu-iotests/271
index 2775b4d130..c7c2cadda0 100755
--- a/tests/qemu-iotests/271
+++ b/tests/qemu-iotests/271
@@ -896,7 +896,7 @@ _make_test_img -o extended_l2=on 1M
# Second and third writes in _concurrent_io() are independent and may finish in
# different order. So, filter offset out to match both possible variants.
_concurrent_io | $QEMU_IO | _filter_qemu_io | \
- $SED -e 's/\(20480\|40960\)/OFFSET/'
+ sed -e 's/\(20480\|40960\)/OFFSET/'
_concurrent_verify | $QEMU_IO | _filter_qemu_io
# success, all done
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
index 099a3eeaa5..f80ef3434a 100755
--- a/tests/qemu-iotests/296
+++ b/tests/qemu-iotests/296
@@ -174,8 +174,12 @@ class EncryptionSetupTestCase(iotests.QMPTestCase):
}
result = vm.qmp('x-blockdev-amend', **args)
- assert result['return'] == {}
- vm.run_job('job0')
+ iotests.log(result)
+ # Run the job only if it was created
+ event = ('JOB_STATUS_CHANGE',
+ {'data': {'id': 'job0', 'status': 'created'}})
+ if vm.events_wait([event], timeout=0.0) is not None:
+ vm.run_job('job0')
# test that when the image opened by two qemu processes,
# neither of them can update the encryption keys
diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out
index 42205cc981..609826eaa0 100644
--- a/tests/qemu-iotests/296.out
+++ b/tests/qemu-iotests/296.out
@@ -1,11 +1,9 @@
-{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-Job failed: Failed to get shared "consistent read" lock
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-Job failed: Failed to get shared "consistent read" lock
-{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}}
+{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -13,14 +11,9 @@ qemu-img: Failed to get shared "consistent read" lock
Is another process using the image [TEST_DIR/test.img]?
.
-Job failed: Block node is read-only
-{"execute": "job-dismiss", "arguments": {"id": "job0"}}
-{"return": {}}
-Job failed: Failed to get shared "consistent read" lock
-{"execute": "job-dismiss", "arguments": {"id": "job0"}}
-{"return": {}}
-Job failed: Failed to get shared "consistent read" lock
-{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"error": {"class": "GenericError", "desc": "Block node is read-only"}}
+{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}}
+{"error": {"class": "GenericError", "desc": "Failed to get shared \"consistent read\" lock"}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index 75cc241580..21819db9c3 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -21,44 +21,44 @@
_filter_date()
{
- $SED -re 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/yyyy-mm-dd hh:mm:ss/'
+ sed -Ee 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/yyyy-mm-dd hh:mm:ss/'
}
_filter_vmstate_size()
{
- $SED -r -e 's/[0-9. ]{5} [KMGT]iB/ SIZE/' \
- -e 's/[0-9. ]{5} B/ SIZE/'
+ sed -E -e 's/[0-9. ]{5} [KMGT]iB/ SIZE/' \
+ -e 's/[0-9. ]{5} B/ SIZE/'
}
_filter_generated_node_ids()
{
- $SED -re 's/\#block[0-9]{3,}/NODE_NAME/'
+ sed -Ee 's/\#block[0-9]{3,}/NODE_NAME/'
}
_filter_qom_path()
{
- $SED -e '/Attached to:/s/\device[[0-9]\+\]/device[N]/g'
+ gsed -e '/Attached to:/s/\device[[0-9]\+\]/device[N]/g'
}
# replace occurrences of the actual TEST_DIR value with TEST_DIR
_filter_testdir()
{
- $SED -e "s#$TEST_DIR/#TEST_DIR/#g" \
- -e "s#$SOCK_DIR/#SOCK_DIR/#g" \
- -e "s#SOCK_DIR/fuse-#TEST_DIR/#g"
+ sed -e "s#$TEST_DIR/#TEST_DIR/#g" \
+ -e "s#$SOCK_DIR/#SOCK_DIR/#g" \
+ -e "s#SOCK_DIR/fuse-#TEST_DIR/#g"
}
# replace occurrences of the actual IMGFMT value with IMGFMT
_filter_imgfmt()
{
- $SED -e "s#$IMGFMT#IMGFMT#g"
+ sed -e "s#$IMGFMT#IMGFMT#g"
}
# Replace error message when the format is not supported and delete
# the output lines after the first one
_filter_qemu_img_check()
{
- $SED -e '/allocated.*fragmented.*compressed clusters/d' \
+ gsed -e '/allocated.*fragmented.*compressed clusters/d' \
-e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \
-e '/Image end offset: [0-9]\+/d'
}
@@ -66,13 +66,14 @@ _filter_qemu_img_check()
# Removes \r from messages
_filter_win32()
{
- $SED -e 's/\r//g'
+ gsed -e 's/\r//g'
}
# sanitize qemu-io output
_filter_qemu_io()
{
- _filter_win32 | $SED -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \
+ _filter_win32 | \
+ gsed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \
-e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \
-e "s/qemu-io> //g"
}
@@ -80,7 +81,7 @@ _filter_qemu_io()
# replace occurrences of QEMU_PROG with "qemu"
_filter_qemu()
{
- $SED -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \
+ gsed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \
-e $'s#\r##' # QEMU monitor uses \r\n line endings
}
@@ -89,7 +90,7 @@ _filter_qemu()
_filter_qmp()
{
_filter_win32 | \
- $SED -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \
+ gsed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \
-e 's#^{"QMP":.*}$#QMP_VERSION#' \
-e '/^ "QMP": {\s*$/, /^ }\s*$/ c\' \
-e ' QMP_VERSION'
@@ -98,32 +99,32 @@ _filter_qmp()
# readline makes HMP command strings so long that git complains
_filter_hmp()
{
- $SED -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \
+ gsed -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \
-e $'s/\e\\[K//g'
}
# replace block job offset
_filter_block_job_offset()
{
- $SED -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/'
+ sed -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/'
}
# replace block job len
_filter_block_job_len()
{
- $SED -e 's/, "len": [0-9]\+,/, "len": LEN,/g'
+ sed -e 's/, "len": [0-9]\+,/, "len": LEN,/g'
}
# replace actual image size (depends on the host filesystem)
_filter_actual_image_size()
{
- $SED -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g'
+ gsed -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g'
}
# Filename filters for qemu-img create
_filter_img_create_filenames()
{
- $SED \
+ sed \
-e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
@@ -141,7 +142,7 @@ _do_filter_img_create()
# precedes ", fmt=") and the options part ($options, which starts
# with "fmt=")
# (And just echo everything before the first "^Formatting")
- readarray formatting_line < <($SED -e 's/, fmt=/\n/')
+ readarray formatting_line < <(gsed -e 's/, fmt=/\n/')
filename_part=${formatting_line[0]}
unset formatting_line[0]
@@ -168,11 +169,11 @@ _do_filter_img_create()
options=$(
echo "$options" \
| tr '\n' '\0' \
- | $SED -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \
+ | gsed -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \
| grep -a -e '^fmt' -e '^size' -e '^backing' -e '^preallocation' \
-e '^encryption' "${grep_data_file[@]}" \
| _filter_img_create_filenames \
- | $SED \
+ | sed \
-e 's/^\(fmt\)/0-\1/' \
-e 's/^\(size\)/1-\1/' \
-e 's/^\(backing\)/2-\1/' \
@@ -180,9 +181,9 @@ _do_filter_img_create()
-e 's/^\(encryption\)/4-\1/' \
-e 's/^\(preallocation\)/8-\1/' \
| LC_ALL=C sort \
- | $SED -e 's/^[0-9]-//' \
+ | sed -e 's/^[0-9]-//' \
| tr '\n\0' ' \n' \
- | $SED -e 's/^ *$//' -e 's/ *$//'
+ | sed -e 's/^ *$//' -e 's/ *$//'
)
if [ -n "$options" ]; then
@@ -208,7 +209,7 @@ _filter_img_create()
_filter_img_create_size()
{
- $SED -e "s# size=[0-9]\\+# size=SIZE#g"
+ gsed -e "s# size=[0-9]\\+# size=SIZE#g"
}
_filter_img_info()
@@ -222,7 +223,7 @@ _filter_img_info()
discard=0
regex_json_spec_start='^ *"format-specific": \{'
- $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
+ gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$SOCK_DIR#SOCK_DIR#g" \
@@ -284,7 +285,7 @@ _filter_qemu_img_map()
data_file_filter=(-e "s#$data_file_pattern#\\1#")
fi
- $SED -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
+ sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \
-e 's/"offset": [0-9]\+/"offset": OFFSET/g' \
-e 's/Mapped to *//' \
"${data_file_filter[@]}" \
@@ -298,7 +299,7 @@ _filter_nbd()
# receive callbacks sometimes, making them unreliable.
#
# Filter out the TCP port number since this changes between runs.
- $SED -e '/nbd\/.*\.c:/d' \
+ sed -e '/nbd\/.*\.c:/d' \
-e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \
-e "s#?socket=$SOCK_DIR#?socket=SOCK_DIR#g" \
-e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#'
@@ -335,14 +336,14 @@ sys.stdout.write(result)'
_filter_authz_check_tls()
{
- $SED -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/'
+ sed -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/'
}
_filter_qcow2_compression_type_bit()
{
- $SED -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \
- -e 's/\(incompatible_features.*\), 3\]/\1]/' \
- -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/'
+ gsed -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \
+ -e 's/\(incompatible_features.*\), 3\]/\1]/' \
+ -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/'
}
# make sure this script returns success
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 9885030b43..3bfd94c2e0 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -17,17 +17,28 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-SED=
-for sed in sed gsed; do
- ($sed --version | grep 'GNU sed') > /dev/null 2>&1
- if [ "$?" -eq 0 ]; then
- SED=$sed
- break
- fi
-done
-if [ -z "$SED" ]; then
- echo "$0: GNU sed not found"
- exit 1
+# bail out, setting up .notrun file
+_notrun()
+{
+ echo "$*" >"$OUTPUT_DIR/$seq.notrun"
+ echo "$seq not run: $*"
+ status=0
+ exit
+}
+
+if ! command -v gsed >/dev/null 2>&1; then
+ if sed --version 2>&1 | grep -v 'not GNU sed' | grep 'GNU sed' > /dev/null;
+ then
+ gsed()
+ {
+ sed "$@"
+ }
+ else
+ gsed()
+ {
+ _notrun "GNU sed not available"
+ }
+ fi
fi
dd()
@@ -722,16 +733,6 @@ _img_info()
done
}
-# bail out, setting up .notrun file
-#
-_notrun()
-{
- echo "$*" >"$OUTPUT_DIR/$seq.notrun"
- echo "$seq not run: $*"
- status=0
- exit
-}
-
# bail out, setting up .casenotrun file
# The function _casenotrun() is used as a notifier. It is the
# caller's responsibility to make skipped a particular test.
@@ -920,7 +921,7 @@ _require_working_luks()
IMGFMT='luks' _rm_test_img "$file"
if [ $status != 0 ]; then
- reason=$(echo "$output" | grep "$file:" | $SED -e "s#.*$file: *##")
+ reason=$(echo "$output" | grep "$file:" | sed -e "s#.*$file: *##")
if [ -z "$reason" ]; then
reason="Failed to create a LUKS image"
fi
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 6ba65eb1ff..6027780180 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -39,6 +39,7 @@ from contextlib import contextmanager
from qemu.machine import qtest
from qemu.qmp import QMPMessage
+from qemu.aqmp.legacy import QEMUMonitorProtocol
# Use this logger for logging messages directly from the iotests module
logger = logging.getLogger('qemu.iotests')
@@ -348,14 +349,30 @@ class QemuIoInteractive:
class QemuStorageDaemon:
- def __init__(self, *args: str, instance_id: str = 'a'):
+ _qmp: Optional[QEMUMonitorProtocol] = None
+ _qmpsock: Optional[str] = None
+ # Python < 3.8 would complain if this type were not a string literal
+ # (importing `annotations` from `__future__` would work; but not on <= 3.6)
+ _p: 'Optional[subprocess.Popen[bytes]]' = None
+
+ def __init__(self, *args: str, instance_id: str = 'a', qmp: bool = False):
assert '--pidfile' not in args
self.pidfile = os.path.join(test_dir, f'qsd-{instance_id}-pid')
all_args = [qsd_prog] + list(args) + ['--pidfile', self.pidfile]
+ if qmp:
+ self._qmpsock = os.path.join(sock_dir, f'qsd-{instance_id}.sock')
+ all_args += ['--chardev',
+ f'socket,id=qmp-sock,path={self._qmpsock}',
+ '--monitor', 'qmp-sock']
+
+ self._qmp = QEMUMonitorProtocol(self._qmpsock, server=True)
+
# Cannot use with here, we want the subprocess to stay around
# pylint: disable=consider-using-with
self._p = subprocess.Popen(all_args)
+ if self._qmp is not None:
+ self._qmp.accept()
while not os.path.exists(self.pidfile):
if self._p.poll() is not None:
cmd = ' '.join(all_args)
@@ -370,11 +387,24 @@ class QemuStorageDaemon:
assert self._pid == self._p.pid
+ def qmp(self, cmd: str, args: Optional[Dict[str, object]] = None) \
+ -> QMPMessage:
+ assert self._qmp is not None
+ return self._qmp.cmd(cmd, args)
+
def stop(self, kill_signal=15):
self._p.send_signal(kill_signal)
self._p.wait()
self._p = None
+ if self._qmp:
+ self._qmp.close()
+
+ if self._qmpsock is not None:
+ try:
+ os.remove(self._qmpsock)
+ except OSError:
+ pass
try:
os.remove(self.pidfile)
except OSError:
diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io
new file mode 100755
index 0000000000..567e8cf21e
--- /dev/null
+++ b/tests/qemu-iotests/tests/graph-changes-while-io
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# group: rw
+#
+# Test graph changes while I/O is happening
+#
+# Copyright (C) 2022 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/>.
+#
+
+import os
+from threading import Thread
+import iotests
+from iotests import imgfmt, qemu_img, qemu_img_create, QMPTestCase, \
+ QemuStorageDaemon
+
+
+top = os.path.join(iotests.test_dir, 'top.img')
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock')
+
+
+def do_qemu_img_bench() -> None:
+ """
+ Do some I/O requests on `nbd_sock`.
+ """
+ assert qemu_img('bench', '-f', 'raw', '-c', '2000000',
+ f'nbd+unix:///node0?socket={nbd_sock}') == 0
+
+
+class TestGraphChangesWhileIO(QMPTestCase):
+ def setUp(self) -> None:
+ # Create an overlay that can be added at runtime on top of the
+ # null-co block node that will receive I/O
+ assert qemu_img_create('-f', imgfmt, '-F', 'raw', '-b', 'null-co://',
+ top) == 0
+
+ # QSD instance with a null-co block node in an I/O thread,
+ # exported over NBD (on `nbd_sock`, export name "node0")
+ self.qsd = QemuStorageDaemon(
+ '--object', 'iothread,id=iothread0',
+ '--blockdev', 'null-co,node-name=node0,read-zeroes=true',
+ '--nbd-server', f'addr.type=unix,addr.path={nbd_sock}',
+ '--export', 'nbd,id=exp0,node-name=node0,iothread=iothread0,' +
+ 'fixed-iothread=true,writable=true',
+ qmp=True
+ )
+
+ def tearDown(self) -> None:
+ self.qsd.stop()
+
+ def test_blockdev_add_while_io(self) -> None:
+ # Run qemu-img bench in the background
+ bench_thr = Thread(target=do_qemu_img_bench)
+ bench_thr.start()
+
+ # While qemu-img bench is running, repeatedly add and remove an
+ # overlay to/from node0
+ while bench_thr.is_alive():
+ result = self.qsd.qmp('blockdev-add', {
+ 'driver': imgfmt,
+ 'node-name': 'overlay',
+ 'backing': 'node0',
+ 'file': {
+ 'driver': 'file',
+ 'filename': top
+ }
+ })
+ self.assert_qmp(result, 'return', {})
+
+ result = self.qsd.qmp('blockdev-del', {
+ 'node-name': 'overlay'
+ })
+ self.assert_qmp(result, 'return', {})
+
+ bench_thr.join()
+
+if __name__ == '__main__':
+ # Format must support raw backing files
+ iotests.main(supported_fmts=['qcow', 'qcow2', 'qed'],
+ supported_protocols=['file'])
diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out
new file mode 100644
index 0000000000..ae1213e6f8
--- /dev/null
+++ b/tests/qemu-iotests/tests/graph-changes-while-io.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
diff --git a/tests/unit/rcutorture.c b/tests/unit/rcutorture.c
index de6f649058..495a4e6f42 100644
--- a/tests/unit/rcutorture.c
+++ b/tests/unit/rcutorture.c
@@ -122,7 +122,7 @@ static void *rcu_read_perf_test(void *arg)
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
qatomic_inc(&nthreadsrunning);
while (goflag == GOFLAG_INIT) {
g_usleep(1000);
@@ -148,7 +148,7 @@ static void *rcu_update_perf_test(void *arg)
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
qatomic_inc(&nthreadsrunning);
while (goflag == GOFLAG_INIT) {
g_usleep(1000);
@@ -253,7 +253,7 @@ static void *rcu_read_stress_test(void *arg)
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
while (goflag == GOFLAG_INIT) {
g_usleep(1000);
}
@@ -304,7 +304,7 @@ static void *rcu_update_stress_test(void *arg)
struct rcu_stress *cp = qatomic_read(&rcu_stress_current);
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
while (goflag == GOFLAG_INIT) {
g_usleep(1000);
@@ -347,7 +347,7 @@ static void *rcu_fake_update_stress_test(void *arg)
{
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
while (goflag == GOFLAG_INIT) {
g_usleep(1000);
}
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
index aea660aeed..94718c9319 100644
--- a/tests/unit/test-block-iothread.c
+++ b/tests/unit/test-block-iothread.c
@@ -279,10 +279,10 @@ static void test_sync_op_check(BdrvChild *c)
g_assert_cmpint(ret, ==, -ENOTSUP);
}
-static void test_sync_op_invalidate_cache(BdrvChild *c)
+static void test_sync_op_activate(BdrvChild *c)
{
/* Early success: Image is not inactive */
- bdrv_invalidate_cache(c->bs, NULL);
+ bdrv_activate(c->bs, NULL);
}
@@ -325,8 +325,8 @@ const SyncOpTest sync_op_tests[] = {
.name = "/sync-op/check",
.fn = test_sync_op_check,
}, {
- .name = "/sync-op/invalidate_cache",
- .fn = test_sync_op_invalidate_cache,
+ .name = "/sync-op/activate",
+ .fn = test_sync_op_activate,
},
};
diff --git a/tests/unit/test-rcu-list.c b/tests/unit/test-rcu-list.c
index 49641e1936..64b81ae058 100644
--- a/tests/unit/test-rcu-list.c
+++ b/tests/unit/test-rcu-list.c
@@ -171,7 +171,7 @@ static void *rcu_q_reader(void *arg)
rcu_register_thread();
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
qatomic_inc(&nthreadsrunning);
while (qatomic_read(&goflag) == GOFLAG_INIT) {
g_usleep(1000);
@@ -206,7 +206,7 @@ static void *rcu_q_updater(void *arg)
long long n_removed_local = 0;
struct list_element *el, *prev_el;
- *(struct rcu_reader_data **)arg = &rcu_reader;
+ *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
qatomic_inc(&nthreadsrunning);
while (qatomic_read(&goflag) == GOFLAG_INIT) {
g_usleep(1000);