From 7299550b252c2c88ae078030428435cf83315dd4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 11 Apr 2012 11:21:25 +0200 Subject: [PATCH 01/38] qemu-iotests: Always filter cluster_size out in _make_test_img Some image formats do have a cluster size, others don't, but there are tests that work with both sets of images and currently we get failures because the qemu-img create output doesn't mention the cluster size for some formats. Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini --- tests/qemu-iotests/013.out | 2 +- tests/qemu-iotests/014.out | 2 +- tests/qemu-iotests/015.out | 2 +- tests/qemu-iotests/019.out | 4 +- tests/qemu-iotests/022.out | 2 +- tests/qemu-iotests/023.out | 16 +-- tests/qemu-iotests/024.out | 6 +- tests/qemu-iotests/026.out | 208 +++++++++++++++++------------------ tests/qemu-iotests/029.out | 4 +- tests/qemu-iotests/031.out | 2 +- tests/qemu-iotests/common.rc | 4 +- 11 files changed, 125 insertions(+), 127 deletions(-) diff --git a/tests/qemu-iotests/013.out b/tests/qemu-iotests/013.out index 3073e3fac7..0d57187e0a 100644 --- a/tests/qemu-iotests/013.out +++ b/tests/qemu-iotests/013.out @@ -1,5 +1,5 @@ QA output created by 013 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 0: diff --git a/tests/qemu-iotests/014.out b/tests/qemu-iotests/014.out index 8045613288..0258d75a3a 100644 --- a/tests/qemu-iotests/014.out +++ b/tests/qemu-iotests/014.out @@ -1,5 +1,5 @@ QA output created by 014 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image: test2: With offset 0 === Clusters to be compressed [1] diff --git a/tests/qemu-iotests/015.out b/tests/qemu-iotests/015.out index 3ba723db38..d4b961c82c 100644 --- a/tests/qemu-iotests/015.out +++ b/tests/qemu-iotests/015.out @@ -1,7 +1,7 @@ QA output created by 015 creating image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=37748736 creating first snapshot wrote 37748736/37748736 bytes at offset 0 36 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/019.out b/tests/qemu-iotests/019.out index 241d30c8b0..b51224b504 100644 --- a/tests/qemu-iotests/019.out +++ b/tests/qemu-iotests/019.out @@ -1,5 +1,5 @@ QA output created by 019 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 42 @@ -269,7 +269,7 @@ qemu-io> wrote 65536/65536 bytes at offset 4296015872 qemu-io> No errors were found on the image. Creating test image with backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' Filling test image === IO: pattern 43 diff --git a/tests/qemu-iotests/022.out b/tests/qemu-iotests/022.out index b900c71af1..aed86d57e5 100644 --- a/tests/qemu-iotests/022.out +++ b/tests/qemu-iotests/022.out @@ -1,5 +1,5 @@ QA output created by 022 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 Testing empty image At offset 10485760: diff --git a/tests/qemu-iotests/023.out b/tests/qemu-iotests/023.out index 138434ee82..8a7c5b2ffb 100644 --- a/tests/qemu-iotests/023.out +++ b/tests/qemu-iotests/023.out @@ -1,7 +1,7 @@ QA output created by 023 Creating new image; cluster size: 1024 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -5664,7 +5664,7 @@ qemu-io> read 3072/3072 bytes at offset 4295491072 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -5887,7 +5887,7 @@ qemu-io> read 2048/2048 bytes at offset 4295001088 qemu-io> No errors were found on the image. Creating new image; cluster size: 4096 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -12270,7 +12270,7 @@ qemu-io> read 12288/12288 bytes at offset 4301256704 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=4096 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -12493,7 +12493,7 @@ qemu-io> read 8192/8192 bytes at offset 4295102464 qemu-io> No errors were found on the image. Creating new image; cluster size: 16384 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=16384 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -18876,7 +18876,7 @@ qemu-io> read 49152/49152 bytes at offset 4395622400 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=16384 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 @@ -19099,7 +19099,7 @@ qemu-io> read 32768/32768 bytes at offset 4295507968 qemu-io> No errors were found on the image. Creating new image; cluster size: 65536 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 Testing empty image At offset 0: @@ -25482,7 +25482,7 @@ qemu-io> read 196608/196608 bytes at offset 5905547264 qemu-io> No errors were found on the image. Creating another new image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8589934592 More complex patterns test2: With offset 0 diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 4cee2165b2..072207c8d4 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -1,7 +1,7 @@ QA output created by 024 Creating backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x11 qemu-io> wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -21,7 +21,7 @@ qemu-io> wrote 65536/65536 bytes at offset 917504 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io> Creating new backing file -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 0x22 qemu-io> wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -33,7 +33,7 @@ qemu-io> wrote 131072/131072 bytes at offset 786432 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io> Creating COW image -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file='TEST_DIR/t.IMGFMT.base_old' === IO: pattern 0x33 qemu-io> wrote 262144/262144 bytes at offset 0 256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index b503cf2575..fb4f20e7cd 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -1,63 +1,63 @@ QA output created by 026 Errors while writing 128 kB -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 5; imm: off; once: off; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_update; errno: 28; imm: off; once: off; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 @@ -65,7 +65,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -73,7 +73,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 @@ -81,7 +81,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 5; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 @@ -89,7 +89,7 @@ wrote 131072/131072 bytes at offset 0 write failed: Input/output error read failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: on; write wrote 131072/131072 bytes at offset 0 @@ -97,7 +97,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: on; write -b wrote 131072/131072 bytes at offset 0 @@ -105,7 +105,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write wrote 131072/131072 bytes at offset 0 @@ -113,7 +113,7 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_load; errno: 28; imm: off; once: off; write -b wrote 131072/131072 bytes at offset 0 @@ -121,306 +121,306 @@ wrote 131072/131072 bytes at offset 0 write failed: No space left on device read failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: on; write -b write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 5; imm: off; once: off; write -b write failed: Input/output error 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: on; write -b write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_update; errno: 28; imm: off; once: off; write -b write failed: No space left on device 128 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b write failed: Input/output error 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b write failed: No space left on device 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: write_aio; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_load; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_update_part; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: on; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: on; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 5; imm: off; once: off; write -b write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: cluster_alloc; errno: 28; imm: off; once: off; write -b write failed: No space left on device @@ -428,116 +428,116 @@ No errors were found on the image. === Refcout table growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write write failed: No space left on device 55 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b write failed: No space left on device 251 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: off; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b write failed: No space left on device 23 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write write failed: No space left on device 10 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=512 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b write failed: No space left on device @@ -547,54 +547,54 @@ This means waste of disk space, but no harm to data. === L1 growth tests === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 5; imm: off; once: off write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.alloc_table; errno: 28; imm: off; once: off write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 5; imm: off; once: off qcow2_free_clusters failed: Input/output error write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.write_table; errno: 28; imm: off; once: off qcow2_free_clusters failed: No space left on device write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: on write failed: Input/output error No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 5; imm: off; once: off qcow2_free_clusters failed: Input/output error @@ -602,12 +602,12 @@ write failed: Input/output error 96 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: on write failed: No space left on device No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: l1_grow.activate_table; errno: 28; imm: off; once: off qcow2_free_clusters failed: No space left on device diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out index c2ad30cefd..0eedb3a3ab 100644 --- a/tests/qemu-iotests/029.out +++ b/tests/qemu-iotests/029.out @@ -1,9 +1,9 @@ QA output created by 029 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 cluster_size=1024 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 wrote 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index 0f1bf685f3..e1a536ec00 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -2,7 +2,7 @@ QA output created by 031 === Create image with unknown header extension === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 cluster_size=65536 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 magic 0x514649fb version 2 backing_file_offset 0x0 diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 4cb8dae8c6..00ee754c8d 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -58,7 +58,6 @@ _make_test_img() # extra qemu-img options can be added by tests # at least one argument (the image size) needs to be added local extra_img_options="" - local cluster_size_filter="s# cluster_size=[0-9]\\+##g" local image_size=$* if [ "$1" = "-b" ]; then @@ -67,7 +66,6 @@ _make_test_img() fi if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then extra_img_options="-o cluster_size=$CLUSTER_SIZE $extra_img_options" - cluster_size_filter="" fi # XXX(hch): have global image options? @@ -76,7 +74,7 @@ _make_test_img() sed -e "s#$TEST_DIR#TEST_DIR#g" | \ sed -e "s#$IMGFMT#IMGFMT#g" | \ sed -e "s# encryption=off##g" | \ - sed -e "$cluster_size_filter" | \ + sed -e "s# cluster_size=[0-9]\\+##g" | \ sed -e "s# table_size=0##g" | \ sed -e "s# compat6=off##g" | \ sed -e "s# static=off##g" From aafcdcc9ebd72b24bf8686f624ff98bb919de5fd Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 11 Apr 2012 10:45:51 +0200 Subject: [PATCH 02/38] qemu-iotests: Test bdrv_close while AIO is in flight If the BlockDriverState is closed/freed without draining the AIO requests first, the request coroutines may work on invalid data and file descriptors or have some dangling pointers that cause segfaults. Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini --- tests/qemu-iotests/032 | 69 +++++++++++++++++++++++++++++++++ tests/qemu-iotests/032.out | 78 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 148 insertions(+) create mode 100755 tests/qemu-iotests/032 create mode 100644 tests/qemu-iotests/032.out diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032 new file mode 100755 index 0000000000..7155568a4f --- /dev/null +++ b/tests/qemu-iotests/032 @@ -0,0 +1,69 @@ +#!/bin/bash +# +# Test that AIO requests are drained before an image is closed. This used +# to segfault because the request coroutine kept running even after the +# BlockDriverState was freed. +# +# Copyright (C) 2011 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# This works for any image format (though unlikely to segfault for raw) +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +echo +echo === Prepare image === +echo + +CLUSTER_SIZE=65536 +_make_test_img 64M + +# Allocate every other cluster so that afterwards a big write request will +# actually loop a while and issue many I/O requests for the lower layer +for i in $(seq 0 128 4096); do echo "write ${i}k 64k"; done | $QEMU_IO $TEST_IMG | _filter_qemu_io + +echo +echo === AIO request during close === +echo +$QEMU_IO -c "aio_write 0 4M" -c "close" $TEST_IMG | _filter_qemu_io +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/032.out b/tests/qemu-iotests/032.out new file mode 100644 index 0000000000..7272ac2133 --- /dev/null +++ b/tests/qemu-iotests/032.out @@ -0,0 +1,78 @@ +QA output created by 032 + +=== Prepare image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io> wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 393216 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 524288 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 655360 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 786432 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 917504 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1179648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1310720 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1441792 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1572864 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1703936 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1835008 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 1966080 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2097152 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2228224 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2359296 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2490368 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2621440 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2752512 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 2883584 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3014656 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3145728 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3276800 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3407872 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3538944 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3670016 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3801088 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 3932160 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 4063232 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 65536/65536 bytes at offset 4194304 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> +=== AIO request during close === + +wrote 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 1742ede180..92508a748e 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -38,3 +38,4 @@ 029 rw auto quick 030 rw auto 031 rw auto quick +032 rw auto From 7094f12f868fc1b75b105bcc676e15964dab77af Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 11 Apr 2012 11:06:37 +0200 Subject: [PATCH 03/38] block: Drain requests in bdrv_close If an AIO request is in flight that refers to a BlockDriverState that has been closed and possibly even freed, more or less anything could happen. I have seen segfaults, -EBADF return values and qcow2 sometimes actually catches the situation in bdrv_close() and abort()s. Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini --- block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block.c b/block.c index c0c90f061b..4b96654e7d 100644 --- a/block.c +++ b/block.c @@ -816,6 +816,8 @@ void bdrv_close(BlockDriverState *bs) if (bs->job) { block_job_cancel_sync(bs->job); } + bdrv_drain_all(); + if (bs == bs_snapshots) { bs_snapshots = NULL; } From 8ff9ae00da148ca98248eb2284ba432970e232ec Mon Sep 17 00:00:00 2001 From: Dong Xu Wang Date: Tue, 17 Apr 2012 16:23:44 +0800 Subject: [PATCH 04/38] iotests: fix error in 005 According comment, we should not read again, we will write. Signed-off-by: Dong Xu Wang Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- tests/qemu-iotests/005 | 2 +- tests/qemu-iotests/005.out | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index 74537dbabb..b7970e3b58 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -65,7 +65,7 @@ $QEMU_IO -c "read 1024 4096" $TEST_IMG | _filter_qemu_io echo echo "small write" -$QEMU_IO -c "read 8192 4096" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write 8192 4096" $TEST_IMG | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/005.out b/tests/qemu-iotests/005.out index 718a18532d..2d3e7df25a 100644 --- a/tests/qemu-iotests/005.out +++ b/tests/qemu-iotests/005.out @@ -8,6 +8,6 @@ read 4096/4096 bytes at offset 1024 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) small write -read 4096/4096 bytes at offset 8192 +wrote 4096/4096 bytes at offset 8192 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done From 4e35b92a51571002a68d3be74b774546d9aefd19 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Tue, 17 Apr 2012 19:41:08 +0200 Subject: [PATCH 05/38] block: Fix spelling in comment (ineffcient -> inefficient) Signed-off-by: Stefan Weil Signed-off-by: Kevin Wolf --- block/cow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/cow.c b/block/cow.c index 8d3c9f873c..a5a00eb9ca 100644 --- a/block/cow.c +++ b/block/cow.c @@ -103,7 +103,7 @@ static int cow_open(BlockDriverState *bs, int flags) } /* - * XXX(hch): right now these functions are extremely ineffcient. + * XXX(hch): right now these functions are extremely inefficient. * We should just read the whole bitmap we'll need in one go instead. */ static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) From 592fa07043095ba3141fb9d413693d3c202cba9a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 18 Apr 2012 12:07:39 +0200 Subject: [PATCH 06/38] qemu-io: Add command line switch for cache mode To be used as in 'qemu-io -t writeback test.img' Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini --- qemu-io.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qemu-io.c b/qemu-io.c index e6fcd7719e..3095a22be3 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -1784,6 +1784,7 @@ static void usage(const char *name) " -g, --growable allow file to grow (only applies to protocols)\n" " -m, --misalign misalign allocations for O_DIRECT\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n" +" -t, --cache=MODE use the given cache mode for the image\n" " -T, --trace FILE enable trace events listed in the given file\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" @@ -1796,7 +1797,7 @@ int main(int argc, char **argv) { int readonly = 0; int growable = 0; - const char *sopt = "hVc:rsnmgkT:"; + const char *sopt = "hVc:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1808,6 +1809,7 @@ int main(int argc, char **argv) { "misalign", 0, NULL, 'm' }, { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, + { "cache", 1, NULL, 't' }, { "trace", 1, NULL, 'T' }, { NULL, 0, NULL, 0 } }; @@ -1840,6 +1842,12 @@ int main(int argc, char **argv) case 'k': flags |= BDRV_O_NATIVE_AIO; break; + case 't': + if (bdrv_parse_cache_flags(optarg, &flags) < 0) { + error_report("Invalid cache option: %s", optarg); + exit(1); + } + break; case 'T': if (!trace_backend_init(optarg, NULL)) { exit(1); /* error message will have been printed */ From bef0fd5958120542f126f2dedbfce65d8839a94d Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 29 Mar 2012 10:31:30 +0100 Subject: [PATCH 07/38] ide: convert ide_sector_read() to asynchronous I/O The IDE PIO interface currently uses bdrv_read() to perform reads synchronously. Synchronous I/O in the vcpu thread is bad because it prevents the guest from executing code - it makes the guest unresponsive. This patch converts IDE PIO to use bdrv_aio_readv(). We simply need to use the BUSY_STAT status so the guest knows to wait while we are busy. The only external user of ide_sector_read() is restart behavior on I/O errors and it is not affected by this change. We still need to restart I/O in the same way. Migration is also unaffected if I understand the code correctly. We continue to use the same transfer function and the BUSY_STAT status should never be migrated since we flush I/O before migrating device state. Signed-off-by: Stefan Hajnoczi Reviewed-by: Paolo Bonzini Reviewed-by: Zhi Yong Wu Tested-by: Richard Davies Signed-off-by: Kevin Wolf --- hw/ide/core.c | 82 +++++++++++++++++++++++++++++++++-------------- hw/ide/internal.h | 3 ++ 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 35723fd800..9894b3b695 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -471,40 +471,68 @@ static void ide_rw_error(IDEState *s) { ide_set_irq(s->bus); } +static void ide_sector_read_cb(void *opaque, int ret) +{ + IDEState *s = opaque; + int n; + + s->pio_aiocb = NULL; + s->status &= ~BUSY_STAT; + + bdrv_acct_done(s->bs, &s->acct); + if (ret != 0) { + if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY | + BM_STATUS_RETRY_READ)) { + return; + } + } + + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + + /* Allow the guest to read the io_buffer */ + ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read); + + ide_set_irq(s->bus); + + ide_set_sector(s, ide_get_sector(s) + n); + s->nsector -= n; +} + void ide_sector_read(IDEState *s) { int64_t sector_num; - int ret, n; + int n; s->status = READY_STAT | SEEK_STAT; s->error = 0; /* not needed by IDE spec, but needed by Windows */ sector_num = ide_get_sector(s); n = s->nsector; - if (n == 0) { - /* no more sector to read from disk */ - ide_transfer_stop(s); - } else { -#if defined(DEBUG_IDE) - printf("read sector=%" PRId64 "\n", sector_num); -#endif - if (n > s->req_nb_sectors) - n = s->req_nb_sectors; - bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); - bdrv_acct_done(s->bs, &s->acct); - if (ret != 0) { - if (ide_handle_rw_error(s, -ret, - BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ)) - { - return; - } - } - ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); - ide_set_irq(s->bus); - ide_set_sector(s, sector_num + n); - s->nsector -= n; + if (n == 0) { + ide_transfer_stop(s); + return; } + + s->status |= BUSY_STAT; + + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + +#if defined(DEBUG_IDE) + printf("sector=%" PRId64 "\n", sector_num); +#endif + + s->iov.iov_base = s->io_buffer; + s->iov.iov_len = n * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&s->qiov, &s->iov, 1); + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + s->pio_aiocb = bdrv_aio_readv(s->bs, sector_num, &s->qiov, n, + ide_sector_read_cb, s); } static void dma_buf_commit(IDEState *s) @@ -1765,6 +1793,12 @@ static void ide_reset(IDEState *s) #ifdef DEBUG_IDE printf("ide: reset\n"); #endif + + if (s->pio_aiocb) { + bdrv_aio_cancel(s->pio_aiocb); + s->pio_aiocb = NULL; + } + if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; else diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 100efd3076..f8a027d0e4 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -385,6 +385,9 @@ struct IDEState { int cd_sector_size; int atapi_dma; /* true if dma is requested for the packet cmd */ BlockAcctCookie acct; + BlockDriverAIOCB *pio_aiocb; + struct iovec iov; + QEMUIOVector qiov; /* ATA DMA state */ int io_buffer_size; QEMUSGList sg; From e82dabd82e7a8ce0294bce829b3d2dd25eb3a514 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 29 Mar 2012 10:31:31 +0100 Subject: [PATCH 08/38] ide: convert ide_sector_write() to asynchronous I/O The IDE PIO write sector code path uses bdrv_write() and hence can make the guest unresponsive while the I/O request is in progress. This patch converts ide_sector_write() to use bdrv_aio_writev() by using the BUSY_STAT bit to tell the guest that the request is in progress. Signed-off-by: Stefan Hajnoczi Reviewed-by: Paolo Bonzini Reviewed-by: Zhi Yong Wu Tested-by: Richard Davies Signed-off-by: Kevin Wolf --- hw/ide/core.c | 61 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 9894b3b695..a5216a6aac 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -688,40 +688,39 @@ static void ide_sector_write_timer_cb(void *opaque) ide_set_irq(s->bus); } -void ide_sector_write(IDEState *s) +static void ide_sector_write_cb(void *opaque, int ret) { - int64_t sector_num; - int ret, n, n1; + IDEState *s = opaque; + int n; - s->status = READY_STAT | SEEK_STAT; - sector_num = ide_get_sector(s); -#if defined(DEBUG_IDE) - printf("write sector=%" PRId64 "\n", sector_num); -#endif - n = s->nsector; - if (n > s->req_nb_sectors) - n = s->req_nb_sectors; - - bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); bdrv_acct_done(s->bs, &s->acct); + s->pio_aiocb = NULL; + s->status &= ~BUSY_STAT; + if (ret != 0) { - if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY)) + if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY)) { return; + } } + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } s->nsector -= n; if (s->nsector == 0) { /* no more sectors to write */ ide_transfer_stop(s); } else { - n1 = s->nsector; - if (n1 > s->req_nb_sectors) + int n1 = s->nsector; + if (n1 > s->req_nb_sectors) { n1 = s->req_nb_sectors; - ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write); + } + ide_transfer_start(s, s->io_buffer, n1 * BDRV_SECTOR_SIZE, + ide_sector_write); } - ide_set_sector(s, sector_num + n); + ide_set_sector(s, ide_get_sector(s) + n); if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { /* It seems there is a bug in the Windows 2000 installer HDD @@ -737,6 +736,30 @@ void ide_sector_write(IDEState *s) } } +void ide_sector_write(IDEState *s) +{ + int64_t sector_num; + int n; + + s->status = READY_STAT | SEEK_STAT | BUSY_STAT; + sector_num = ide_get_sector(s); +#if defined(DEBUG_IDE) + printf("sector=%" PRId64 "\n", sector_num); +#endif + n = s->nsector; + if (n > s->req_nb_sectors) { + n = s->req_nb_sectors; + } + + s->iov.iov_base = s->io_buffer; + s->iov.iov_len = n * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&s->qiov, &s->iov, 1); + + bdrv_acct_start(s->bs, &s->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + s->pio_aiocb = bdrv_aio_writev(s->bs, sector_num, &s->qiov, n, + ide_sector_write_cb, s); +} + static void ide_flush_cb(void *opaque, int ret) { IDEState *s = opaque; From 8dc0a5e7a06c059683f9c379c0a4b0bbc20d5c74 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 18 Apr 2012 16:18:14 +0200 Subject: [PATCH 09/38] qcow2: Fix error handling in qcow2_alloc_cluster_offset If do_alloc_cluster_offset() fails, the error handling code tried to remove the request from the in-flight queue, to which it wasn't added yet, resulting in a NULL pointer dereference. m->nb_clusters really only becomes != 0 when the request is in the list. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index cbd224dc46..dcf70a24d3 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -931,7 +931,7 @@ again: fail: qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); fail_put: - if (nb_clusters > 0) { + if (m->nb_clusters > 0) { QLIST_REMOVE(m, next_in_flight); } return ret; From 2795ecf681107d55e4113592b3045ece5f6e7b3b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 18 Apr 2012 16:27:06 +0200 Subject: [PATCH 10/38] qcow2: Fix return value of alloc_refcount_block Someone forgot something in commit 29c1a730... Documenting the right return value is not enough, you also need to actually return it in the code. This bug sometimes causes error return values even when everything has succeeded: The new offset of the refcount block is truncated to 32 bits and interpreted as signed. At least with small cluster sizes it's easy to get a negative return value this way. Signed-off-by: Kevin Wolf Reviewed-by: Paolo Bonzini --- block/qcow2-refcount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index f39928a6bf..565bd54902 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -400,7 +400,7 @@ static int alloc_refcount_block(BlockDriverState *bs, return ret; } - return new_block; + return 0; fail_table: g_free(new_table); From 3ed9902528d0ffd244310014f7dcdcb3df3e2a04 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 18 Apr 2012 15:12:01 +0200 Subject: [PATCH 11/38] block: allow interrupting a co_sleep_ns In the next patch we want to reenter the coroutine from block_job_cancel_sync and cancel the timer. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- qemu-coroutine-sleep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qemu-coroutine-sleep.c b/qemu-coroutine-sleep.c index fd65274446..d7083ee41a 100644 --- a/qemu-coroutine-sleep.c +++ b/qemu-coroutine-sleep.c @@ -23,7 +23,6 @@ static void co_sleep_cb(void *opaque) { CoSleepCB *sleep_cb = opaque; - qemu_free_timer(sleep_cb->ts); qemu_coroutine_enter(sleep_cb->co, NULL); } @@ -35,4 +34,6 @@ void coroutine_fn co_sleep_ns(QEMUClock *clock, int64_t ns) sleep_cb.ts = qemu_new_timer(clock, SCALE_NS, co_sleep_cb, &sleep_cb); qemu_mod_timer(sleep_cb.ts, qemu_get_clock_ns(clock) + ns); qemu_coroutine_yield(); + qemu_del_timer(sleep_cb.ts); + qemu_free_timer(sleep_cb.ts); } From a5a5238ee49cd6a0f598f6f63a4a43dbe18d12b7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:51 +0200 Subject: [PATCH 12/38] qemu-io: use main_loop_wait This will let timers run during aio_read and aio_write commands, though not during synchronous commands. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- cmd.c | 10 +++++----- qemu-io.c | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cmd.c b/cmd.c index 0806e18ce0..7ffbb71406 100644 --- a/cmd.c +++ b/cmd.c @@ -25,6 +25,7 @@ #include "cmd.h" #include "qemu-aio.h" +#include "main-loop.h" #define _(x) x /* not gettext support yet */ @@ -146,7 +147,7 @@ static void prep_fetchline(void *opaque) { int *fetchable = opaque; - qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); *fetchable= 1; } @@ -193,12 +194,11 @@ void command_loop(void) if (!prompted) { printf("%s", get_prompt()); fflush(stdout); - qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL, - NULL, &fetchable); + qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable); prompted = 1; } - qemu_aio_wait(); + main_loop_wait(false); if (!fetchable) { continue; @@ -221,7 +221,7 @@ void command_loop(void) prompted = 0; fetchable = 0; } - qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); } /* from libxcmd/input.c */ diff --git a/qemu-io.c b/qemu-io.c index 3095a22be3..b48364f1b3 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -15,6 +15,7 @@ #include #include "qemu-common.h" +#include "main-loop.h" #include "block_int.h" #include "cmd.h" #include "trace/control.h" @@ -295,7 +296,7 @@ static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -309,7 +310,7 @@ static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -352,7 +353,7 @@ static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) } while (async_ret.num_done < num_reqs) { - qemu_aio_wait(); + main_loop_wait(false); } return async_ret.error < 0 ? async_ret.error : 1; From 8a83205d34fbd7b12061f4e85148eb4b587acdcc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:52 +0200 Subject: [PATCH 13/38] qemu-tool: map vm_clock to rt_clock QED uses vm_clock timers so that images are not touched during and after migration. This however does not apply to qemu-io and qemu-img. Treat vm_clock as a synonym for rt_clock there, and enable it. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- qemu-tool.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qemu-tool.c b/qemu-tool.c index edb84f5f5d..6579b00552 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -61,7 +61,7 @@ void monitor_protocol_event(MonitorEvent event, QObject *data) int64_t cpu_get_clock(void) { - return 0; + return qemu_get_clock_ns(rt_clock); } int64_t cpu_get_icount(void) @@ -87,7 +87,6 @@ int qemu_init_main_loop(void) { init_clocks(); init_timer_alarm(); - qemu_clock_enable(vm_clock, false); return main_loop_init(); } From adfe92f6d18c0e0a3694e19abb58eb55fd0c5993 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:53 +0200 Subject: [PATCH 14/38] posix-aio: merge posix_aio_process_queue and posix_aio_read posix_aio_read already calls qemu_aio_process_queue, and dually qemu_aio_process_queue is always followed by a select loop that calls posix_aio_read. No races are possible, so there is no need for a separate process_queue callback. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- posix-aio-compat.c | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/posix-aio-compat.c b/posix-aio-compat.c index d311d13890..1066c601d2 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -468,26 +468,37 @@ static int qemu_paio_error(struct qemu_paiocb *aiocb) return ret; } -static int posix_aio_process_queue(void *opaque) +static void posix_aio_read(void *opaque) { PosixAioState *s = opaque; struct qemu_paiocb *acb, **pacb; int ret; - int result = 0; + ssize_t len; + + /* read all bytes from signal pipe */ + for (;;) { + char bytes[16]; + + len = read(s->rfd, bytes, sizeof(bytes)); + if (len == -1 && errno == EINTR) + continue; /* try again */ + if (len == sizeof(bytes)) + continue; /* more to read */ + break; + } for(;;) { pacb = &s->first_aio; for(;;) { acb = *pacb; if (!acb) - return result; + return; ret = qemu_paio_error(acb); if (ret == ECANCELED) { /* remove the request */ *pacb = acb->next; qemu_aio_release(acb); - result = 1; } else if (ret != EINPROGRESS) { /* end of aio */ if (ret == 0) { @@ -507,35 +518,12 @@ static int posix_aio_process_queue(void *opaque) /* call the callback */ acb->common.cb(acb->common.opaque, ret); qemu_aio_release(acb); - result = 1; break; } else { pacb = &acb->next; } } } - - return result; -} - -static void posix_aio_read(void *opaque) -{ - PosixAioState *s = opaque; - ssize_t len; - - /* read all bytes from signal pipe */ - for (;;) { - char bytes[16]; - - len = read(s->rfd, bytes, sizeof(bytes)); - if (len == -1 && errno == EINTR) - continue; /* try again */ - if (len == sizeof(bytes)) - continue; /* more to read */ - break; - } - - posix_aio_process_queue(s); } static int posix_aio_flush(void *opaque) @@ -676,7 +664,7 @@ int paio_init(void) fcntl(s->wfd, F_SETFL, O_NONBLOCK); qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, - posix_aio_process_queue, s); + NULL, s); ret = pthread_attr_init(&attr); if (ret) From bafbd6a1c69fef73500309dc31c86984c6d22b43 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:54 +0200 Subject: [PATCH 15/38] aio: remove process_queue callback and qemu_aio_process_queue Both unused after the previous patch. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- aio.c | 29 ++--------------------------- block/curl.c | 10 ++++------ block/iscsi.c | 4 ++-- block/nbd.c | 8 ++++---- block/rbd.c | 5 ++--- block/sheepdog.c | 11 +++++------ linux-aio.c | 2 +- posix-aio-compat.c | 3 +-- qemu-aio.h | 13 ------------- 9 files changed, 21 insertions(+), 64 deletions(-) diff --git a/aio.c b/aio.c index eb3bf42574..f19b3c6b09 100644 --- a/aio.c +++ b/aio.c @@ -35,7 +35,6 @@ struct AioHandler IOHandler *io_read; IOHandler *io_write; AioFlushHandler *io_flush; - AioProcessQueue *io_process_queue; int deleted; void *opaque; QLIST_ENTRY(AioHandler) node; @@ -58,7 +57,6 @@ int qemu_aio_set_fd_handler(int fd, IOHandler *io_read, IOHandler *io_write, AioFlushHandler *io_flush, - AioProcessQueue *io_process_queue, void *opaque) { AioHandler *node; @@ -91,7 +89,6 @@ int qemu_aio_set_fd_handler(int fd, node->io_read = io_read; node->io_write = io_write; node->io_flush = io_flush; - node->io_process_queue = io_process_queue; node->opaque = opaque; } @@ -122,39 +119,17 @@ void qemu_aio_flush(void) } while (qemu_bh_poll() || ret > 0); } -int qemu_aio_process_queue(void) -{ - AioHandler *node; - int ret = 0; - - walking_handlers = 1; - - QLIST_FOREACH(node, &aio_handlers, node) { - if (node->io_process_queue) { - if (node->io_process_queue(node->opaque)) { - ret = 1; - } - } - } - - walking_handlers = 0; - - return ret; -} - void qemu_aio_wait(void) { int ret; - if (qemu_bh_poll()) - return; - /* * If there are callbacks left that have been queued, we need to call then. * Return afterwards to avoid waiting needlessly in select(). */ - if (qemu_aio_process_queue()) + if (qemu_bh_poll()) { return; + } do { AioHandler *node; diff --git a/block/curl.c b/block/curl.c index a909eca337..bf3680ba57 100644 --- a/block/curl.c +++ b/block/curl.c @@ -89,19 +89,17 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: - qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, - NULL, s); + qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s); break; case CURL_POLL_OUT: - qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, - NULL, s); + qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s); break; case CURL_POLL_INOUT: qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do, - curl_aio_flush, NULL, s); + curl_aio_flush, s); break; case CURL_POLL_REMOVE: - qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL); break; } diff --git a/block/iscsi.c b/block/iscsi.c index bd3ca11b2e..5222726d0f 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -108,7 +108,7 @@ iscsi_set_events(IscsiLun *iscsilun) qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read, (iscsi_which_events(iscsi) & POLLOUT) ? iscsi_process_write : NULL, - iscsi_process_flush, NULL, iscsilun); + iscsi_process_flush, iscsilun); } static void @@ -682,7 +682,7 @@ static void iscsi_close(BlockDriverState *bs) IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; - qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL); iscsi_destroy_context(iscsi); memset(iscsilun, 0, sizeof(IscsiLun)); } diff --git a/block/nbd.c b/block/nbd.c index 161b299855..524c9cf412 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -191,7 +191,7 @@ static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request, qemu_co_mutex_lock(&s->send_mutex); s->send_coroutine = qemu_coroutine_self(); qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, - nbd_have_request, NULL, s); + nbd_have_request, s); rc = nbd_send_request(s->sock, request); if (rc != -1 && iov) { ret = qemu_co_sendv(s->sock, iov, request->len, offset); @@ -201,7 +201,7 @@ static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request, } } qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, - nbd_have_request, NULL, s); + nbd_have_request, s); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); return rc; @@ -274,7 +274,7 @@ static int nbd_establish_connection(BlockDriverState *bs) * kick the reply mechanism. */ socket_set_nonblock(sock); qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, - nbd_have_request, NULL, s); + nbd_have_request, s); s->sock = sock; s->size = size; @@ -294,7 +294,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) request.len = 0; nbd_send_request(s->sock, &request); - qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL); closesocket(s->sock); } diff --git a/block/rbd.c b/block/rbd.c index 46a8579018..6cd84488e4 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -504,7 +504,7 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags) fcntl(s->fds[0], F_SETFL, O_NONBLOCK); fcntl(s->fds[1], F_SETFL, O_NONBLOCK); qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader, - NULL, qemu_rbd_aio_flush_cb, NULL, s); + NULL, qemu_rbd_aio_flush_cb, s); return 0; @@ -525,8 +525,7 @@ static void qemu_rbd_close(BlockDriverState *bs) close(s->fds[0]); close(s->fds[1]); - qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL, - NULL); + qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL, NULL); rbd_close(s->image); rados_ioctx_destroy(s->io_ctx); diff --git a/block/sheepdog.c b/block/sheepdog.c index 3eaf625e98..0ed6b193c9 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -799,8 +799,7 @@ static int get_sheep_fd(BDRVSheepdogState *s) return -1; } - qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, - NULL, s); + qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, s); return fd; } @@ -973,7 +972,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, qemu_co_mutex_lock(&s->lock); s->co_send = qemu_coroutine_self(); qemu_aio_set_fd_handler(s->fd, co_read_response, co_write_request, - aio_flush_request, NULL, s); + aio_flush_request, s); socket_set_cork(s->fd, 1); /* send a header */ @@ -995,7 +994,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, socket_set_cork(s->fd, 0); qemu_aio_set_fd_handler(s->fd, co_read_response, NULL, - aio_flush_request, NULL, s); + aio_flush_request, s); qemu_co_mutex_unlock(&s->lock); return 0; @@ -1135,7 +1134,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) g_free(buf); return 0; out: - qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL); if (s->fd >= 0) { closesocket(s->fd); } @@ -1349,7 +1348,7 @@ static void sd_close(BlockDriverState *bs) error_report("%s, %s", sd_strerror(rsp->result), s->name); } - qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); if (s->cache_enabled) { closesocket(s->flush_fd); diff --git a/linux-aio.c b/linux-aio.c index 15261ece3d..fa0fbf34aa 100644 --- a/linux-aio.c +++ b/linux-aio.c @@ -214,7 +214,7 @@ void *laio_init(void) goto out_close_efd; qemu_aio_set_fd_handler(s->efd, qemu_laio_completion_cb, NULL, - qemu_laio_flush_cb, NULL, s); + qemu_laio_flush_cb, s); return s; diff --git a/posix-aio-compat.c b/posix-aio-compat.c index 1066c601d2..68361f555a 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -663,8 +663,7 @@ int paio_init(void) fcntl(s->rfd, F_SETFL, O_NONBLOCK); fcntl(s->wfd, F_SETFL, O_NONBLOCK); - qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, - NULL, s); + qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, s); ret = pthread_attr_init(&attr); if (ret) diff --git a/qemu-aio.h b/qemu-aio.h index 230c2f79a0..0fc84093c0 100644 --- a/qemu-aio.h +++ b/qemu-aio.h @@ -41,11 +41,6 @@ void qemu_aio_release(void *p); /* Returns 1 if there are still outstanding AIO requests; 0 otherwise */ typedef int (AioFlushHandler)(void *opaque); -/* Runs all currently allowed AIO callbacks of completed requests in the - * respective AIO backend. Returns 0 if no requests was handled, non-zero - * if at least one queued request was handled. */ -typedef int (AioProcessQueue)(void *opaque); - /* Flush any pending AIO operation. This function will block until all * outstanding AIO operations have been completed or cancelled. */ void qemu_aio_flush(void); @@ -56,13 +51,6 @@ void qemu_aio_flush(void); * result of executing I/O completion or bh callbacks. */ void qemu_aio_wait(void); -/* - * Runs all currently allowed AIO callbacks of completed requests. Returns 0 - * if no requests were handled, non-zero if at least one request was - * processed. - */ -int qemu_aio_process_queue(void); - /* Register a file descriptor and associated callbacks. Behaves very similarly * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will * be invoked when using either qemu_aio_wait() or qemu_aio_flush(). @@ -74,7 +62,6 @@ int qemu_aio_set_fd_handler(int fd, IOHandler *io_read, IOHandler *io_write, AioFlushHandler *io_flush, - AioProcessQueue *io_process_queue, void *opaque); #endif From bcdc18578d5b41180db2e17baa7563c5f05b39ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:55 +0200 Subject: [PATCH 16/38] aio: return "AIO in progress" state from qemu_aio_wait The definition of when qemu_aio_flush should loop is much simpler than it looks. It just has to call qemu_aio_wait until it makes no progress and all flush callbacks return false. qemu_aio_wait is the logical place to tell the caller about this. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- aio.c | 44 ++++++++++++++++++-------------------------- qemu-aio.h | 6 ++++-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/aio.c b/aio.c index f19b3c6b09..5fcc0c6b2f 100644 --- a/aio.c +++ b/aio.c @@ -99,41 +99,26 @@ int qemu_aio_set_fd_handler(int fd, void qemu_aio_flush(void) { - AioHandler *node; - int ret; - - do { - ret = 0; - - /* - * If there are pending emulated aio start them now so flush - * will be able to return 1. - */ - qemu_aio_wait(); - - QLIST_FOREACH(node, &aio_handlers, node) { - if (node->io_flush) { - ret |= node->io_flush(node->opaque); - } - } - } while (qemu_bh_poll() || ret > 0); + while (qemu_aio_wait()); } -void qemu_aio_wait(void) +bool qemu_aio_wait(void) { int ret; /* * If there are callbacks left that have been queued, we need to call then. - * Return afterwards to avoid waiting needlessly in select(). + * Do not call select in this case, because it is possible that the caller + * does not need a complete flush (as is the case for qemu_aio_wait loops). */ if (qemu_bh_poll()) { - return; + return true; } do { AioHandler *node; fd_set rdfds, wrfds; + bool busy; int max_fd = -1; walking_handlers = 1; @@ -142,14 +127,18 @@ void qemu_aio_wait(void) FD_ZERO(&wrfds); /* fill fd sets */ + busy = false; QLIST_FOREACH(node, &aio_handlers, node) { /* If there aren't pending AIO operations, don't invoke callbacks. * Otherwise, if there are no AIO requests, qemu_aio_wait() would * wait indefinitely. */ - if (node->io_flush && node->io_flush(node->opaque) == 0) - continue; - + if (node->io_flush) { + if (node->io_flush(node->opaque) == 0) { + continue; + } + busy = true; + } if (!node->deleted && node->io_read) { FD_SET(node->fd, &rdfds); max_fd = MAX(max_fd, node->fd + 1); @@ -163,8 +152,9 @@ void qemu_aio_wait(void) walking_handlers = 0; /* No AIO operations? Get us out of here */ - if (max_fd == -1) - break; + if (!busy) { + return false; + } /* wait until next event */ ret = select(max_fd, &rdfds, &wrfds, NULL, NULL); @@ -204,4 +194,6 @@ void qemu_aio_wait(void) walking_handlers = 0; } } while (ret == 0); + + return true; } diff --git a/qemu-aio.h b/qemu-aio.h index 0fc84093c0..bfdd35f02c 100644 --- a/qemu-aio.h +++ b/qemu-aio.h @@ -48,8 +48,10 @@ void qemu_aio_flush(void); /* Wait for a single AIO completion to occur. This function will wait * until a single AIO event has completed and it will ensure something * has moved before returning. This can issue new pending aio as - * result of executing I/O completion or bh callbacks. */ -void qemu_aio_wait(void); + * result of executing I/O completion or bh callbacks. + * + * Return whether there is still any pending AIO operation. */ +bool qemu_aio_wait(void); /* Register a file descriptor and associated callbacks. Behaves very similarly * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will From 9eb0bfca960b8cf24fb42e017c3a6479e5676f75 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 12 Apr 2012 14:00:56 +0200 Subject: [PATCH 17/38] aio: simplify qemu_aio_wait The do...while loop can never loop, because select will just not return 0 when invoked with infinite timeout. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- aio.c | 133 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 64 insertions(+), 69 deletions(-) diff --git a/aio.c b/aio.c index 5fcc0c6b2f..0a9eb10c76 100644 --- a/aio.c +++ b/aio.c @@ -104,7 +104,11 @@ void qemu_aio_flush(void) bool qemu_aio_wait(void) { + AioHandler *node; + fd_set rdfds, wrfds; + int max_fd = -1; int ret; + bool busy; /* * If there are callbacks left that have been queued, we need to call then. @@ -115,85 +119,76 @@ bool qemu_aio_wait(void) return true; } - do { - AioHandler *node; - fd_set rdfds, wrfds; - bool busy; - int max_fd = -1; + walking_handlers = 1; + FD_ZERO(&rdfds); + FD_ZERO(&wrfds); + + /* fill fd sets */ + busy = false; + QLIST_FOREACH(node, &aio_handlers, node) { + /* If there aren't pending AIO operations, don't invoke callbacks. + * Otherwise, if there are no AIO requests, qemu_aio_wait() would + * wait indefinitely. + */ + if (node->io_flush) { + if (node->io_flush(node->opaque) == 0) { + continue; + } + busy = true; + } + if (!node->deleted && node->io_read) { + FD_SET(node->fd, &rdfds); + max_fd = MAX(max_fd, node->fd + 1); + } + if (!node->deleted && node->io_write) { + FD_SET(node->fd, &wrfds); + max_fd = MAX(max_fd, node->fd + 1); + } + } + + walking_handlers = 0; + + /* No AIO operations? Get us out of here */ + if (!busy) { + return false; + } + + /* wait until next event */ + ret = select(max_fd, &rdfds, &wrfds, NULL, NULL); + + /* if we have any readable fds, dispatch event */ + if (ret > 0) { walking_handlers = 1; - FD_ZERO(&rdfds); - FD_ZERO(&wrfds); + /* we have to walk very carefully in case + * qemu_aio_set_fd_handler is called while we're walking */ + node = QLIST_FIRST(&aio_handlers); + while (node) { + AioHandler *tmp; - /* fill fd sets */ - busy = false; - QLIST_FOREACH(node, &aio_handlers, node) { - /* If there aren't pending AIO operations, don't invoke callbacks. - * Otherwise, if there are no AIO requests, qemu_aio_wait() would - * wait indefinitely. - */ - if (node->io_flush) { - if (node->io_flush(node->opaque) == 0) { - continue; - } - busy = true; + if (!node->deleted && + FD_ISSET(node->fd, &rdfds) && + node->io_read) { + node->io_read(node->opaque); } - if (!node->deleted && node->io_read) { - FD_SET(node->fd, &rdfds); - max_fd = MAX(max_fd, node->fd + 1); + if (!node->deleted && + FD_ISSET(node->fd, &wrfds) && + node->io_write) { + node->io_write(node->opaque); } - if (!node->deleted && node->io_write) { - FD_SET(node->fd, &wrfds); - max_fd = MAX(max_fd, node->fd + 1); + + tmp = node; + node = QLIST_NEXT(node, node); + + if (tmp->deleted) { + QLIST_REMOVE(tmp, node); + g_free(tmp); } } walking_handlers = 0; - - /* No AIO operations? Get us out of here */ - if (!busy) { - return false; - } - - /* wait until next event */ - ret = select(max_fd, &rdfds, &wrfds, NULL, NULL); - if (ret == -1 && errno == EINTR) - continue; - - /* if we have any readable fds, dispatch event */ - if (ret > 0) { - walking_handlers = 1; - - /* we have to walk very carefully in case - * qemu_aio_set_fd_handler is called while we're walking */ - node = QLIST_FIRST(&aio_handlers); - while (node) { - AioHandler *tmp; - - if (!node->deleted && - FD_ISSET(node->fd, &rdfds) && - node->io_read) { - node->io_read(node->opaque); - } - if (!node->deleted && - FD_ISSET(node->fd, &wrfds) && - node->io_write) { - node->io_write(node->opaque); - } - - tmp = node; - node = QLIST_NEXT(node, node); - - if (tmp->deleted) { - QLIST_REMOVE(tmp, node); - g_free(tmp); - } - } - - walking_handlers = 0; - } - } while (ret == 0); + } return true; } From 80ccf93b884a2edab5ec62634758e942bba81b7c Mon Sep 17 00:00:00 2001 From: Liu Yuan Date: Fri, 20 Apr 2012 17:10:56 +0800 Subject: [PATCH 18/38] qemu-img: let 'qemu-img convert' flush data The 'qemu-img convert -h' advertise that the default cache mode is 'writeback', while in fact it is 'unsafe'. This patch 1) fix the help manual and 2) let bdrv_close() call bdrv_flush() 2) is needed because some backend storage doesn't have a self-flush mechanism(for e.g., sheepdog), so we need to call bdrv_flush() to make sure the image is really writen to the storage instead of hanging around writeback cache forever. Signed-off-by: Liu Yuan Signed-off-by: Kevin Wolf --- block.c | 1 + qemu-img.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 4b96654e7d..421cd1bebd 100644 --- a/block.c +++ b/block.c @@ -812,6 +812,7 @@ unlink_and_fail: void bdrv_close(BlockDriverState *bs) { + bdrv_flush(bs); if (bs->drv) { if (bs->job) { block_job_cancel_sync(bs->job); diff --git a/qemu-img.c b/qemu-img.c index 6a61ca8d06..0ae543cade 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -66,8 +66,8 @@ static void help(void) " 'filename' is a disk image filename\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" " 'cache' is the cache mode used to write the output disk image, the valid\n" - " options are: 'none', 'writeback' (default), 'writethrough', 'directsync'\n" - " and 'unsafe'\n" + " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" + " 'directsync' and 'unsafe' (default for convert)\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" From 29926112a2fd619abedd8e21002f5d50c476b402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Fri, 20 Apr 2012 12:50:24 +0200 Subject: [PATCH 19/38] iotests: Resolve test failures caused by hostname MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `hostname -s` may output an errror: hostname: Name or service not known This causes all tests to fail for `make check-block`. Suppress such error messages, letting the tests succeed. Signed-off-by: Andreas Färber Signed-off-by: Kevin Wolf --- tests/qemu-iotests/common.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index a220684b0f..df082e750c 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -36,7 +36,7 @@ export LANG=C PATH=".:$PATH" -HOST=`hostname -s` +HOST=`hostname -s 2> /dev/null` HOSTOS=`uname -s` EMAIL=root@localhost # where auto-qa will send its status messages From f24423bd902bce29bc546cf8d030bfa369726ab1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 20 Apr 2012 15:50:39 +0200 Subject: [PATCH 20/38] qcow2: Fix refcount block allocation during qcow2_alloc_cluster_at() Refcount block allocation and refcount table growth rely on s->free_cluster_index pointing to somewhere after the current allocation. Change qcow2_alloc_cluster_at() to fulfill this assumption. Without this change it could happen that a newly allocated refcount block and the allocated data block point to the same area in the image file, causing data corruption in the long run. This fixes a bug that became first visible after commit 250196f1. Signed-off-by: Kevin Wolf --- block/qcow2-refcount.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 565bd54902..6c383373de 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -587,6 +587,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, { BDRVQcowState *s = bs->opaque; uint64_t cluster_index; + uint64_t old_free_cluster_index; int i, refcount, ret; /* Check how many clusters there are free */ @@ -602,11 +603,16 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, } /* And then allocate them */ + old_free_cluster_index = s->free_cluster_index; + s->free_cluster_index = cluster_index + i; + ret = update_refcount(bs, offset, i << s->cluster_bits, 1); if (ret < 0) { return ret; } + s->free_cluster_index = old_free_cluster_index; + return i; } From 4fabffc11234d0587a213418574095e036110cd2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2012 13:20:41 +0200 Subject: [PATCH 21/38] Specification for qcow2 version 3 This updates the qcow2 specification to cover version 3. It contains the following changes: - Added compatible/incompatible/auto-clear feature bits plus an optional feature name table to allow useful error messages even if an older version doesn't know some feature at all. - Configurable refcount width. If you don't want to use internal snapshots, make refcounts one bit and save cache space and I/O. - Zero cluster flags. This allows discard even with a backing file that doesn't contain zeros. It is also useful for copy-on-read/image streaming, as you'll want to keep sparseness without accessing the remote image for an unallocated cluster all the time. - Fixed internal snapshot metadata to use 64 bit VM state size. You can't save a snapshot of a VM with >= 4 GB RAM today. - Extended internal snapshot metadata to contain the disk size, so that resizing images that have snapshots can be allowed in the future. Signed-off-by: Kevin Wolf --- docs/specs/qcow2.txt | 122 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 23 deletions(-) diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt index b6adcaddb7..65e632555f 100644 --- a/docs/specs/qcow2.txt +++ b/docs/specs/qcow2.txt @@ -18,7 +18,7 @@ The first cluster of a qcow2 image contains the file header: QCOW magic string ("QFI\xfb") 4 - 7: version - Version number (only valid value is 2) + Version number (valid values are 2 and 3) 8 - 15: backing_file_offset Offset into the image file at which the backing file name @@ -67,12 +67,45 @@ The first cluster of a qcow2 image contains the file header: Offset into the image file at which the snapshot table starts. Must be aligned to a cluster boundary. +If the version is 3 or higher, the header has the following additional fields. +For version 2, the values are assumed to be zero, unless specified otherwise +in the description of a field. + + 72 - 79: incompatible_features + Bitmask of incompatible features. An implementation must + fail to open an image if an unknown bit is set. + + Bits 0-63: Reserved (set to 0) + + 80 - 87: compatible_features + Bitmask of compatible features. An implementation can + safely ignore any unknown bits that are set. + + Bits 0-63: Reserved (set to 0) + + 88 - 95: autoclear_features + Bitmask of auto-clear features. An implementation may only + write to an image with unknown auto-clear features if it + clears the respective bits from this field first. + + Bits 0-63: Reserved (set to 0) + + 96 - 99: refcount_order + Describes the width of a reference count block entry (width + in bits = 1 << refcount_order). For version 2 images, the + order is always assumed to be 4 (i.e. the width is 16 bits). + + 100 - 103: header_length + Length of the header structure in bytes. For version 2 + images, the length is always assumed to be 72 bytes. + Directly after the image header, optional sections called header extensions can be stored. Each extension has a structure like the following: Byte 0 - 3: Header extension type: 0x00000000 - End of the header extension area 0xE2792ACA - Backing file format name + 0x6803f857 - Feature name table other - Unknown header extension, can be safely ignored @@ -83,9 +116,37 @@ be stored. Each extension has a structure like the following: n - m: Padding to round up the header extension size to the next multiple of 8. +Unless stated otherwise, each header extension type shall appear at most once +in the same image. + The remaining space between the end of the header extension area and the end of -the first cluster can be used for other data. Usually, the backing file name is -stored there. +the first cluster can be used for the backing file name. It is not allowed to +store other data here, so that an implementation can safely modify the header +and add extensions without harming data of compatible features that it +doesn't support. Compatible features that need space for additional data can +use a header extension. + + +== Feature name table == + +The feature name table is an optional header extension that contains the name +for features used by the image. It can be used by applications that don't know +the respective feature (e.g. because the feature was introduced only later) to +display a useful error message. + +The number of entries in the feature name table is determined by the length of +the header extension data. Each entry look like this: + + Byte 0: Type of feature (select feature bitmap) + 0: Incompatible feature + 1: Compatible feature + 2: Autoclear feature + + 1: Bit number within the selected feature bitmap (valid + values: 0-63) + + 2 - 47: Feature name (padded with zeros, but not necessarily null + terminated if it has full length) == Host cluster management == @@ -126,9 +187,11 @@ Refcount table entry: been allocated. All refcounts managed by this refcount block are 0. -Refcount block entry: +Refcount block entry (x = refcount_bits - 1): - Bit 0 - 15: Reference count of the cluster + Bit 0 - x: Reference count of the cluster. If refcount_bits implies a + sub-byte width, note that bit 0 means the least significant + bit in this context. == Cluster mapping == @@ -168,9 +231,29 @@ L1 table entry: refcount is exactly one. This information is only accurate in the active L1 table. -L2 table entry (for normal clusters): +L2 table entry: - Bit 0 - 8: Reserved (set to 0) + Bit 0 - 61: Cluster descriptor + + 62: 0 for standard clusters + 1 for compressed clusters + + 63: 0 for a cluster that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in L2 tables that are reachable from the the active L1 + table. + +Standard Cluster Descriptor: + + Bit 0: If set to 1, the cluster reads as all zeros. The host + cluster offset can be used to describe a preallocation, + but it won't be used for reading data from this cluster, + nor is data read from the backing file if the cluster is + unallocated. + + With version 2, this is always 0. + + 1 - 8: Reserved (set to 0) 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a cluster boundary. If the offset is 0, the cluster is @@ -178,30 +261,18 @@ L2 table entry (for normal clusters): 56 - 61: Reserved (set to 0) - 62: 0 (this cluster is not compressed) - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the the active L1 - table. - -L2 table entry (for compressed clusters; x = 62 - (cluster_size - 8)): +Compressed Clusters Descriptor (x = 62 - (cluster_size - 8)): Bit 0 - x: Host cluster offset. This is usually _not_ aligned to a cluster boundary! x+1 - 61: Compressed size of the images in sectors of 512 bytes - 62: 1 (this cluster is compressed using zlib) - - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the the active L1 - table. - If a cluster is unallocated, read requests shall read the data from the backing -file. If there is no backing file or the backing file is smaller than the image, -they shall read zeros for all parts that are not covered by the backing file. +file (except if bit 0 in the Standard Cluster Descriptor is set). If there is +no backing file or the backing file is smaller than the image, they shall read +zeros for all parts that are not covered by the backing file. == Snapshots == @@ -261,6 +332,11 @@ Snapshot table entry: state is saved. If this field is present, the 32-bit value in bytes 32-35 is ignored. + Byte 48 - 55: Virtual disk size of the snapshot in bytes + + Version 3 images must include extra data at least up to + byte 55. + variable: Unique ID string for the snapshot (not null terminated) variable: Name of the snapshot (not null terminated) From 90b277593df873d3a2480f002e2eb5fe1f8e5277 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 11 Apr 2012 16:33:50 +0200 Subject: [PATCH 22/38] qcow2: Save disk size in snapshot header This allows that different snapshots of an image can have different sizes, which is a requirement for enabling image resizing even with images that have internal snapshots. We don't do the actual support for it now, but make sure that the additional field is present and not completely ignored in all version 3 images. When trying to load a snapshot of different size, it returns an error. Signed-off-by: Kevin Wolf --- block/qcow2-snapshot.c | 16 ++++++++++++++++ block/qcow2.h | 1 + 2 files changed, 17 insertions(+) diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 7d3fde5a8a..42f971b590 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -48,6 +48,7 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { typedef struct QEMU_PACKED QCowSnapshotExtraData { uint64_t vm_state_size_large; + uint64_t disk_size; } QCowSnapshotExtraData; void qcow2_free_snapshots(BlockDriverState *bs) @@ -117,6 +118,12 @@ int qcow2_read_snapshots(BlockDriverState *bs) sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); } + if (extra_data_size >= 16) { + sn->disk_size = be64_to_cpu(extra.disk_size); + } else { + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + } + /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); @@ -197,6 +204,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) memset(&extra, 0, sizeof(extra)); extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); + extra.disk_size = cpu_to_be64(sn->disk_size); id_str_size = strlen(sn->id_str); name_size = strlen(sn->name); @@ -330,6 +338,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) sn->id_str = g_strdup(sn_info->id_str); sn->name = g_strdup(sn_info->name); + sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; sn->vm_state_size = sn_info->vm_state_size; sn->date_sec = sn_info->date_sec; sn->date_nsec = sn_info->date_nsec; @@ -426,6 +435,13 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) } sn = &s->snapshots[snapshot_index]; + if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { + error_report("qcow2: Loading snapshots with different disk " + "size is not implemented"); + ret = -ENOTSUP; + goto fail; + } + /* * Make sure that the current L1 table is big enough to contain the whole * L1 table of the snapshot. If the snapshot L1 table is smaller, the diff --git a/block/qcow2.h b/block/qcow2.h index e4ac366cfc..ddb976a759 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -78,6 +78,7 @@ typedef struct QCowSnapshot { uint32_t l1_size; char *id_str; char *name; + uint64_t disk_size; uint64_t vm_state_size; uint32_t date_sec; uint32_t date_nsec; From 68d000a39074fe3888680491444a7fde2354cd84 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 14 Mar 2012 19:15:03 +0100 Subject: [PATCH 23/38] qcow2: Ignore reserved bits in get_cluster_offset With this change, reading from a qcow2 image ignores all reserved bits that are set in an L1 or L2 table entry. Now get_cluster_offset() assigns *cluster_offset only the offset without any other flags. The cluster type is not longer encoded in the offset, but a positive return value in case of success. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 41 +++++++++++++++++++++++++---------------- block/qcow2.c | 17 ++++++++++++++--- block/qcow2.h | 21 +++++++++++++++++++++ 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index dcf70a24d3..c8c85d224a 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -367,11 +367,9 @@ out: * * on exit, *num is the number of contiguous sectors we can read. * - * Return 0, if the offset is found - * Return -errno, otherwise. - * + * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error + * cases. */ - int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset) { @@ -407,19 +405,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* seek the the l2 offset in the l1 table */ l1_index = offset >> l1_bits; - if (l1_index >= s->l1_size) + if (l1_index >= s->l1_size) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } - l2_offset = s->l1_table[l1_index]; - - /* seek the l2 table of the given l2 offset */ - - if (!l2_offset) + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + if (!l2_offset) { + ret = QCOW2_CLUSTER_UNALLOCATED; goto out; + } /* load the l2 table in memory */ - l2_offset &= ~QCOW_OFLAG_COPIED; ret = l2_load(bs, l2_offset, &l2_table); if (ret < 0) { return ret; @@ -431,26 +429,37 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, *cluster_offset = be64_to_cpu(l2_table[l2_index]); nb_clusters = size_to_clusters(s, nb_needed << 9); - if (!*cluster_offset) { + ret = qcow2_get_cluster_type(*cluster_offset); + switch (ret) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters can only be processed one by one */ + c = 1; + *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; + break; + case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); - } else { + *cluster_offset = 0; + break; + case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); + *cluster_offset &= L2E_OFFSET_MASK; + break; } qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - nb_available = (c * s->cluster_sectors); + nb_available = (c * s->cluster_sectors); + out: if (nb_available > nb_needed) nb_available = nb_needed; *num = nb_available - index_in_cluster; - *cluster_offset &=~QCOW_OFLAG_COPIED; - return 0; + return ret; } /* diff --git a/block/qcow2.c b/block/qcow2.c index 70d3141dd1..4c82adc96f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -449,7 +449,8 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_copy(&hd_qiov, qiov, bytes_done, cur_nr_sectors * 512); - if (!cluster_offset) { + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: if (bs->backing_hd) { /* read from the base image */ @@ -469,7 +470,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, /* Note: in this case, no need to wait */ qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); } - } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + break; + + case QCOW2_CLUSTER_COMPRESSED: /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); if (ret < 0) { @@ -479,7 +482,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, s->cluster_cache + index_in_cluster * 512, 512 * cur_nr_sectors); - } else { + break; + + case QCOW2_CLUSTER_NORMAL: if ((cluster_offset & 511) != 0) { ret = -EIO; goto fail; @@ -520,6 +525,12 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_from_buffer(&hd_qiov, cluster_data, 512 * cur_nr_sectors); } + break; + + default: + g_assert_not_reached(); + ret = -EIO; + goto fail; } remaining_sectors -= cur_nr_sectors; diff --git a/block/qcow2.h b/block/qcow2.h index ddb976a759..a22d7fafbe 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -165,6 +165,16 @@ typedef struct QCowL2Meta QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; +enum { + QCOW2_CLUSTER_UNALLOCATED, + QCOW2_CLUSTER_NORMAL, + QCOW2_CLUSTER_COMPRESSED, +}; + +#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL +#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL + static inline int size_to_clusters(BDRVQcowState *s, int64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; @@ -182,6 +192,17 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } +static inline int qcow2_get_cluster_type(uint64_t l2_entry) +{ + if (l2_entry & QCOW_OFLAG_COMPRESSED) { + return QCOW2_CLUSTER_COMPRESSED; + } else if (!(l2_entry & L2E_OFFSET_MASK)) { + return QCOW2_CLUSTER_UNALLOCATED; + } else { + return QCOW2_CLUSTER_NORMAL; + } +} + // FIXME Need qcow2_ prefix to global functions From 2bfcc4a0a0b5120b6518e1c823c18cf0e8b963cb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 15 Mar 2012 16:37:40 +0100 Subject: [PATCH 24/38] qcow2: Ignore reserved bits in count_contiguous_clusters() Until now, count_contiguous_clusters() has an argument that allowed to specify flags that should be ignored in the comparison, i.e. that are allowed to change between contiguous clusters. This patch changes the function so that it ignores all flags by default now and you need to pass the flags on which it should stop. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index c8c85d224a..7b059908a0 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -246,28 +246,44 @@ fail: return ret; } +/* + * Checks how many clusters in a given L2 table are contiguous in the image + * file. As soon as one of the flags in the bitmask stop_flags changes compared + * to the first cluster, the search is stopped and the cluster is not counted + * as contiguous. (This allows it, for example, to stop at the first compressed + * cluster which may require a different handling) + */ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, - uint64_t *l2_table, uint64_t start, uint64_t mask) + uint64_t *l2_table, uint64_t start, uint64_t stop_flags) { int i; - uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask; + uint64_t mask = stop_flags | L2E_OFFSET_MASK; + uint64_t offset = be64_to_cpu(l2_table[0]) & mask; if (!offset) return 0; - for (i = start; i < start + nb_clusters; i++) - if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask)) + for (i = start; i < start + nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask; + if (offset + (uint64_t) i * cluster_size != l2_entry) { break; + } + } return (i - start); } static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) { - int i = 0; + int i; - while(nb_clusters-- && l2_table[i] == 0) - i++; + for (i = 0; i < nb_clusters; i++) { + int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i])); + + if (type != QCOW2_CLUSTER_UNALLOCATED) { + break; + } + } return i; } @@ -444,7 +460,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); + &l2_table[l2_index], 0, QCOW_OFLAG_COMPRESSED); *cluster_offset &= L2E_OFFSET_MASK; break; } @@ -696,7 +712,8 @@ static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, while (i < nb_clusters) { i += count_contiguous_clusters(nb_clusters - i, s->cluster_size, - &l2_table[l2_index], i, 0); + &l2_table[l2_index], i, + QCOW_OFLAG_COPIED | QCOW_OFLAG_COMPRESSED); if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) { break; } @@ -854,7 +871,8 @@ again: if (cluster_offset & QCOW_OFLAG_COPIED) { /* We keep all QCOW_OFLAG_COPIED clusters */ keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, 0); + &l2_table[l2_index], 0, + QCOW_OFLAG_COPIED); assert(keep_clusters <= nb_clusters); nb_clusters -= keep_clusters; } else { From b0b6862e5e1a1394e0ab3d5da94ba8b0da8664e2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 15 Mar 2012 17:20:11 +0100 Subject: [PATCH 25/38] qcow2: Fail write_compressed when overwriting data qcow2_alloc_compressed_cluster_offset() already fails if the copied flag is set, because qcow2_write_compressed() doesn't perform COW as it would have to do to allow this. However, what we really want to check here is whether the cluster is allocated or not. With internal snapshots the copied flag may not be set on allocated clusters. Check the cluster offset instead. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 7b059908a0..dff782fc21 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -571,15 +571,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, return 0; } + /* Compression can't overwrite anything. Fail if the cluster was already + * allocated. */ cluster_offset = be64_to_cpu(l2_table[l2_index]); - if (cluster_offset & QCOW_OFLAG_COPIED) { + if (cluster_offset & L2E_OFFSET_MASK) { qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); return 0; } - if (cluster_offset) - qcow2_free_any_clusters(bs, cluster_offset, 1); - cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); From 8e37f681d58bbe166c20559e77fd55b1cb8e6e4b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 23 Feb 2012 15:40:55 +0100 Subject: [PATCH 26/38] qcow2: Ignore reserved bits in L1/L2 entries This changes the still existing places that assume that the only flags are QCOW_OFLAG_COPIED and QCOW_OFLAG_COMPRESSED to properly mask out reserved bits. It does not convert bdrv_check yet. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 26 +++++++++++++------------- block/qcow2-refcount.c | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index dff782fc21..d1602d4bdd 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -195,7 +195,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) l2_table = *table; - if (old_l2_offset == 0) { + if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { /* if there was no old l2 table, clear the new table */ memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); } else { @@ -203,7 +203,8 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) /* if there was an old l2 table, read it from the disk */ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); - ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, + ret = qcow2_cache_get(bs, s->l2_table_cache, + old_l2_offset & L1E_OFFSET_MASK, (void**) &old_table); if (ret < 0) { goto fail; @@ -508,13 +509,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, return ret; } } - l2_offset = s->l1_table[l1_index]; + + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; /* seek the l2 table of the given l2 offset */ - if (l2_offset & QCOW_OFLAG_COPIED) { + if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) { /* load the l2 table in memory */ - l2_offset &= ~QCOW_OFLAG_COPIED; ret = l2_load(bs, l2_offset, &l2_table); if (ret < 0) { return ret; @@ -530,7 +531,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, if (l2_offset) { qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); } - l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; + l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; } /* find the cluster offset for the given disk offset */ @@ -687,8 +688,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) */ if (j != 0) { for (i = 0; i < j; i++) { - qcow2_free_any_clusters(bs, - be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1); } } @@ -867,7 +867,9 @@ again: * Check how many clusters are already allocated and don't need COW, and how * many need a new allocation. */ - if (cluster_offset & QCOW_OFLAG_COPIED) { + if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL + && (cluster_offset & QCOW_OFLAG_COPIED)) + { /* We keep all QCOW_OFLAG_COPIED clusters */ keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, &l2_table[l2_index], 0, @@ -886,7 +888,7 @@ again: cluster_offset = 0; } - cluster_offset &= ~QCOW_OFLAG_COPIED; + cluster_offset &= L2E_OFFSET_MASK; /* If there is something left to allocate, do that now */ *m = (QCowL2Meta) { @@ -1041,9 +1043,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, uint64_t old_offset; old_offset = be64_to_cpu(l2_table[l2_index + i]); - old_offset &= ~QCOW_OFLAG_COPIED; - - if (old_offset == 0) { + if ((old_offset & L2E_OFFSET_MASK) == 0) { continue; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 6c383373de..84fa343248 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -702,9 +702,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, return; } - qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits); - - return; + qcow2_free_clusters(bs, cluster_offset & L2E_OFFSET_MASK, + nb_clusters << s->cluster_bits); } @@ -764,7 +763,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l2_offset = l1_table[i]; if (l2_offset) { old_l2_offset = l2_offset; - l2_offset &= ~QCOW_OFLAG_COPIED; + l2_offset &= L1E_OFFSET_MASK; ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) &l2_table); @@ -796,10 +795,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, /* compressed clusters are never modified */ refcount = 2; } else { + uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits; if (addend != 0) { - refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend); + refcount = update_cluster_refcount(bs, cluster_index, addend); } else { - refcount = get_refcount(bs, offset >> s->cluster_bits); + refcount = get_refcount(bs, cluster_index); } if (refcount < 0) { From c7a4c37a0f768f7ad50f64414d578436019e1e96 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 27 Mar 2012 13:09:22 +0200 Subject: [PATCH 27/38] qcow2: Refactor qcow2_free_any_clusters Zero clusters will add another cluster type. Refactor the open-coded cluster type detection into a switch of QCOW2_CLUSTER_* options so that the detection is in a single place. This makes it easier to add new cluster types. Signed-off-by: Kevin Wolf --- block/qcow2-refcount.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 84fa343248..909d61560a 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -679,31 +679,34 @@ void qcow2_free_clusters(BlockDriverState *bs, } /* - * free_any_clusters - * - * free clusters according to its type: compressed or not - * + * Free a cluster using its L2 entry (handles clusters of all types, e.g. + * normal cluster, compressed cluster, etc.) */ - void qcow2_free_any_clusters(BlockDriverState *bs, - uint64_t cluster_offset, int nb_clusters) + uint64_t l2_entry, int nb_clusters) { BDRVQcowState *s = bs->opaque; - /* free the cluster */ - - if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - int nb_csectors; - nb_csectors = ((cluster_offset >> s->csize_shift) & - s->csize_mask) + 1; - qcow2_free_clusters(bs, - (cluster_offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512); - return; + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + { + int nb_csectors; + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + qcow2_free_clusters(bs, + (l2_entry & s->cluster_offset_mask) & ~511, + nb_csectors * 512); + } + break; + case QCOW2_CLUSTER_NORMAL: + qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, + nb_clusters << s->cluster_bits); + break; + case QCOW2_CLUSTER_UNALLOCATED: + break; + default: + abort(); } - - qcow2_free_clusters(bs, cluster_offset & L2E_OFFSET_MASK, - nb_clusters << s->cluster_bits); } From 143550a83ef4eef86a847d00023d148e1f59f743 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 27 Mar 2012 13:17:22 +0200 Subject: [PATCH 28/38] qcow2: Simplify count_cow_clusters count_cow_clusters() tries to reuse existing functions, and all it achieves is to make things much more complicated than they really are: Everything needs COW, unless it's a normal cluster with refcount 1. This patch implements the obvious way of doing this, and by using qcow2_get_cluster_type() it gets rid of all flag magic. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index d1602d4bdd..b8836ba009 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -706,30 +706,27 @@ err: static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, uint64_t *l2_table, int l2_index) { - int i = 0; - uint64_t cluster_offset; + int i; - while (i < nb_clusters) { - i += count_contiguous_clusters(nb_clusters - i, s->cluster_size, - &l2_table[l2_index], i, - QCOW_OFLAG_COPIED | QCOW_OFLAG_COMPRESSED); - if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) { + for (i = 0; i < nb_clusters; i++) { + uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]); + int cluster_type = qcow2_get_cluster_type(l2_entry); + + switch(cluster_type) { + case QCOW2_CLUSTER_NORMAL: + if (l2_entry & QCOW_OFLAG_COPIED) { + goto out; + } break; + case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_COMPRESSED: + break; + default: + abort(); } - - i += count_contiguous_free_clusters(nb_clusters - i, - &l2_table[l2_index + i]); - if (i >= nb_clusters) { - break; - } - - cluster_offset = be64_to_cpu(l2_table[l2_index + i]); - - if ((cluster_offset & QCOW_OFLAG_COPIED) || - (cluster_offset & QCOW_OFLAG_COMPRESSED)) - break; } +out: assert(i <= nb_clusters); return i; } From 76dc9e0c8f369f1695e5413de2e28d69108476bb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 16 Mar 2012 14:09:08 +0100 Subject: [PATCH 29/38] qcow2: Ignore reserved bits in refcount table entries Signed-off-by: Kevin Wolf --- block/qcow2-refcount.c | 2 +- block/qcow2.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 909d61560a..e0854dc85d 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -167,7 +167,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (refcount_table_index < s->refcount_table_size) { uint64_t refcount_block_offset = - s->refcount_table[refcount_table_index]; + s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; /* If it's already there, we're done */ if (refcount_block_offset) { diff --git a/block/qcow2.h b/block/qcow2.h index a22d7fafbe..291309a9e7 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -175,6 +175,8 @@ enum { #define L2E_OFFSET_MASK 0x00ffffffffffff00ULL #define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL +#define REFT_OFFSET_MASK 0xffffffffffffff00ULL + static inline int size_to_clusters(BDRVQcowState *s, int64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; From afdf0abe779f4b11712eb306ab2d4299820457b8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 27 Mar 2012 13:44:56 +0200 Subject: [PATCH 30/38] qcow2: Ignore reserved bits in check_refcounts Also don't infer the cluster type directly from the L2 entries, but use qcow2_get_cluster_type() to keep everything in a single place. Signed-off-by: Kevin Wolf --- block/qcow2-refcount.c | 102 ++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index e0854dc85d..0112cc37b0 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -940,7 +940,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, int check_copied) { BDRVQcowState *s = bs->opaque; - uint64_t *l2_table, offset; + uint64_t *l2_table, l2_entry; int i, l2_size, nb_csectors, refcount; /* Read L2 table from disk */ @@ -952,54 +952,64 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Do the actual checks */ for(i = 0; i < s->l2_size; i++) { - offset = be64_to_cpu(l2_table[i]); - if (offset != 0) { - if (offset & QCOW_OFLAG_COMPRESSED) { - /* Compressed clusters don't have QCOW_OFLAG_COPIED */ - if (offset & QCOW_OFLAG_COPIED) { - fprintf(stderr, "ERROR: cluster %" PRId64 ": " - "copied flag must never be set for compressed " - "clusters\n", offset >> s->cluster_bits); - offset &= ~QCOW_OFLAG_COPIED; - res->corruptions++; + l2_entry = be64_to_cpu(l2_table[i]); + + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + /* Compressed clusters don't have QCOW_OFLAG_COPIED */ + if (l2_entry & QCOW_OFLAG_COPIED) { + fprintf(stderr, "ERROR: cluster %" PRId64 ": " + "copied flag must never be set for compressed " + "clusters\n", l2_entry >> s->cluster_bits); + l2_entry &= ~QCOW_OFLAG_COPIED; + res->corruptions++; + } + + /* Mark cluster as used */ + nb_csectors = ((l2_entry >> s->csize_shift) & + s->csize_mask) + 1; + l2_entry &= s->cluster_offset_mask; + inc_refcounts(bs, res, refcount_table, refcount_table_size, + l2_entry & ~511, nb_csectors * 512); + break; + + case QCOW2_CLUSTER_NORMAL: + { + /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ + uint64_t offset = l2_entry & L2E_OFFSET_MASK; + + if (check_copied) { + refcount = get_refcount(bs, offset >> s->cluster_bits); + if (refcount < 0) { + fprintf(stderr, "Can't get refcount for offset %" + PRIx64 ": %s\n", l2_entry, strerror(-refcount)); + goto fail; } - - /* Mark cluster as used */ - nb_csectors = ((offset >> s->csize_shift) & - s->csize_mask) + 1; - offset &= s->cluster_offset_mask; - inc_refcounts(bs, res, refcount_table, refcount_table_size, - offset & ~511, nb_csectors * 512); - } else { - /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ - if (check_copied) { - uint64_t entry = offset; - offset &= ~QCOW_OFLAG_COPIED; - refcount = get_refcount(bs, offset >> s->cluster_bits); - if (refcount < 0) { - fprintf(stderr, "Can't get refcount for offset %" - PRIx64 ": %s\n", entry, strerror(-refcount)); - goto fail; - } - if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) { - fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" - PRIx64 " refcount=%d\n", entry, refcount); - res->corruptions++; - } - } - - /* Mark cluster as used */ - offset &= ~QCOW_OFLAG_COPIED; - inc_refcounts(bs, res, refcount_table,refcount_table_size, - offset, s->cluster_size); - - /* Correct offsets are cluster aligned */ - if (offset & (s->cluster_size - 1)) { - fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " - "properly aligned; L2 entry corrupted.\n", offset); + if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { + fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" + PRIx64 " refcount=%d\n", l2_entry, refcount); res->corruptions++; } } + + /* Mark cluster as used */ + inc_refcounts(bs, res, refcount_table,refcount_table_size, + offset, s->cluster_size); + + /* Correct offsets are cluster aligned */ + if (offset & (s->cluster_size - 1)) { + fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " + "properly aligned; L2 entry corrupted.\n", offset); + res->corruptions++; + } + break; + } + + case QCOW2_CLUSTER_UNALLOCATED: + break; + + default: + abort(); } } @@ -1070,7 +1080,7 @@ static int check_refcounts_l1(BlockDriverState *bs, } /* Mark L2 table as used */ - l2_offset &= ~QCOW_OFLAG_COPIED; + l2_offset &= L1E_OFFSET_MASK; inc_refcounts(bs, res, refcount_table, refcount_table_size, l2_offset, s->cluster_size); From 6744cbab8cd63b7ce72b3eee4f0055007acf0798 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 15 Dec 2011 12:20:58 +0100 Subject: [PATCH 31/38] qcow2: Version 3 images This adds the basic infrastructure to qcow2 to handle version 3 images. It includes code to create v3 images, allow header updates for v3 images and checks feature bits. It still misses support for zero clusters, so this is not a fully compliant implementation of v3 yet. The default for creating new images stays at v2 for now. Signed-off-by: Kevin Wolf --- block/qcow2.c | 146 +++++++++++++++++++++++++++++++++++++++++++++----- block/qcow2.h | 17 +++++- block_int.h | 1 + 3 files changed, 149 insertions(+), 15 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 4c82adc96f..3e1482fa7e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -61,7 +61,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) if (buf_size >= sizeof(QCowHeader) && be32_to_cpu(cow_header->magic) == QCOW_MAGIC && - be32_to_cpu(cow_header->version) >= QCOW_VERSION) + be32_to_cpu(cow_header->version) >= 2) return 100; else return 0; @@ -169,6 +169,19 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } +static void report_unsupported(BlockDriverState *bs, const char *fmt, ...) +{ + char msg[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "qcow2", msg); +} + static int qcow2_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; @@ -199,14 +212,64 @@ static int qcow2_open(BlockDriverState *bs, int flags) ret = -EINVAL; goto fail; } - if (header.version != QCOW_VERSION) { - char version[64]; - snprintf(version, sizeof(version), "QCOW version %d", header.version); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "qcow2", version); + if (header.version < 2 || header.version > 3) { + report_unsupported(bs, "QCOW version %d", header.version); ret = -ENOTSUP; goto fail; } + + s->qcow_version = header.version; + + /* Initialise version 3 header fields */ + if (header.version == 2) { + header.incompatible_features = 0; + header.compatible_features = 0; + header.autoclear_features = 0; + header.refcount_order = 4; + header.header_length = 72; + } else { + be64_to_cpus(&header.incompatible_features); + be64_to_cpus(&header.compatible_features); + be64_to_cpus(&header.autoclear_features); + be32_to_cpus(&header.refcount_order); + be32_to_cpus(&header.header_length); + } + + if (header.header_length > sizeof(header)) { + s->unknown_header_fields_size = header.header_length - sizeof(header); + s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); + ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, + s->unknown_header_fields_size); + if (ret < 0) { + goto fail; + } + } + + /* Handle feature bits */ + s->incompatible_features = header.incompatible_features; + s->compatible_features = header.compatible_features; + s->autoclear_features = header.autoclear_features; + + if (s->incompatible_features != 0) { + report_unsupported(bs, "incompatible features mask %" PRIx64, + header.incompatible_features); + ret = -ENOTSUP; + goto fail; + } + + if (!bs->read_only && s->autoclear_features != 0) { + s->autoclear_features = 0; + qcow2_update_header(bs); + } + + /* Check support for various header values */ + if (header.refcount_order != 4) { + report_unsupported(bs, "%d bit reference counts", + 1 << header.refcount_order); + ret = -ENOTSUP; + goto fail; + } + if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { ret = -EINVAL; @@ -285,7 +348,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) } else { ext_end = s->cluster_size; } - if (qcow2_read_extensions(bs, sizeof(header), ext_end)) { + if (qcow2_read_extensions(bs, header.header_length, ext_end)) { ret = -EINVAL; goto fail; } @@ -321,6 +384,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) return ret; fail: + g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); qcow2_refcount_close(bs); @@ -682,7 +746,9 @@ static void qcow2_close(BlockDriverState *bs) qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); + g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); + g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); @@ -756,10 +822,10 @@ int qcow2_update_header(BlockDriverState *bs) int ret; uint64_t total_size; uint32_t refcount_table_clusters; + size_t header_length; Qcow2UnknownHeaderExtension *uext; buf = qemu_blockalign(bs, buflen); - memset(buf, 0, s->cluster_size); /* Header structure */ header = (QCowHeader*) buf; @@ -769,12 +835,14 @@ int qcow2_update_header(BlockDriverState *bs) goto fail; } + header_length = sizeof(*header) + s->unknown_header_fields_size; total_size = bs->total_sectors * BDRV_SECTOR_SIZE; refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); *header = (QCowHeader) { + /* Version 2 fields */ .magic = cpu_to_be32(QCOW_MAGIC), - .version = cpu_to_be32(QCOW_VERSION), + .version = cpu_to_be32(s->qcow_version), .backing_file_offset = 0, .backing_file_size = 0, .cluster_bits = cpu_to_be32(s->cluster_bits), @@ -786,10 +854,42 @@ int qcow2_update_header(BlockDriverState *bs) .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), .nb_snapshots = cpu_to_be32(s->nb_snapshots), .snapshots_offset = cpu_to_be64(s->snapshots_offset), + + /* Version 3 fields */ + .incompatible_features = cpu_to_be64(s->incompatible_features), + .compatible_features = cpu_to_be64(s->compatible_features), + .autoclear_features = cpu_to_be64(s->autoclear_features), + .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), + .header_length = cpu_to_be32(header_length), }; - buf += sizeof(*header); - buflen -= sizeof(*header); + /* For older versions, write a shorter header */ + switch (s->qcow_version) { + case 2: + ret = offsetof(QCowHeader, incompatible_features); + break; + case 3: + ret = sizeof(*header); + break; + default: + return -EINVAL; + } + + buf += ret; + buflen -= ret; + memset(buf, 0, buflen); + + /* Preserve any unknown field in the header */ + if (s->unknown_header_fields_size) { + if (buflen < s->unknown_header_fields_size) { + ret = -ENOSPC; + goto fail; + } + + memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size); + buf += s->unknown_header_fields_size; + buflen -= s->unknown_header_fields_size; + } /* Backing file format header extension */ if (*bs->backing_format) { @@ -921,7 +1021,7 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, int prealloc, - QEMUOptionParameter *options) + QEMUOptionParameter *options, int version) { /* Calculate cluster_bits */ int cluster_bits; @@ -965,13 +1065,15 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Write the header */ memset(&header, 0, sizeof(header)); header.magic = cpu_to_be32(QCOW_MAGIC); - header.version = cpu_to_be32(QCOW_VERSION); + header.version = cpu_to_be32(version); header.cluster_bits = cpu_to_be32(cluster_bits); header.size = cpu_to_be64(0); header.l1_table_offset = cpu_to_be64(0); header.l1_size = cpu_to_be32(0); header.refcount_table_offset = cpu_to_be64(cluster_size); header.refcount_table_clusters = cpu_to_be32(1); + header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT); + header.header_length = cpu_to_be32(sizeof(header)); if (flags & BLOCK_FLAG_ENCRYPT) { header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); @@ -1053,6 +1155,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) int flags = 0; size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; + int version = 2; /* Read out options */ while (options && options->name) { @@ -1078,6 +1181,16 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) options->value.s); return -EINVAL; } + } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) { + if (!options->value.s || !strcmp(options->value.s, "0.10")) { + version = 2; + } else if (!strcmp(options->value.s, "1.1")) { + version = 3; + } else { + fprintf(stderr, "Invalid compatibility level: '%s'\n", + options->value.s); + return -EINVAL; + } } options++; } @@ -1089,7 +1202,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) } return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc, options); + cluster_size, prealloc, options, version); } static int qcow2_make_empty(BlockDriverState *bs) @@ -1340,6 +1453,11 @@ static QEMUOptionParameter qcow2_create_options[] = { .type = OPT_SIZE, .help = "Virtual disk size" }, + { + .name = BLOCK_OPT_COMPAT_LEVEL, + .type = OPT_STRING, + .help = "Compatibility level (0.10 or 1.1)" + }, { .name = BLOCK_OPT_BACKING_FILE, .type = OPT_STRING, diff --git a/block/qcow2.h b/block/qcow2.h index 291309a9e7..2dc8ca26fb 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -33,7 +33,6 @@ //#define DEBUG_EXT #define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) -#define QCOW_VERSION 2 #define QCOW_CRYPT_NONE 0 #define QCOW_CRYPT_AES 1 @@ -71,6 +70,14 @@ typedef struct QCowHeader { uint32_t refcount_table_clusters; uint32_t nb_snapshots; uint64_t snapshots_offset; + + /* The following fields are only valid for version >= 3 */ + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + uint32_t refcount_order; + uint32_t header_length; } QCowHeader; typedef struct QCowSnapshot { @@ -135,6 +142,14 @@ typedef struct BDRVQcowState { QCowSnapshot *snapshots; int flags; + int qcow_version; + + uint64_t incompatible_features; + uint64_t compatible_features; + uint64_t autoclear_features; + + size_t unknown_header_fields_size; + void* unknown_header_fields; QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; } BDRVQcowState; diff --git a/block_int.h b/block_int.h index 0e5a032e77..0acb49f100 100644 --- a/block_int.h +++ b/block_int.h @@ -50,6 +50,7 @@ #define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_PREALLOC "preallocation" #define BLOCK_OPT_SUBFMT "subformat" +#define BLOCK_OPT_COMPAT_LEVEL "compat" typedef struct BdrvTrackedRequest BdrvTrackedRequest; From 6377af48b0445e7ee50db6e0bd73a3a70098f5f4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 16 Mar 2012 15:02:38 +0100 Subject: [PATCH 32/38] qcow2: Support reading zero clusters This adds support for reading zero clusters in version 3 images. Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 17 +++++++++++++---- block/qcow2-refcount.c | 7 +++++++ block/qcow2.c | 8 ++++++++ block/qcow2.h | 5 +++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index b8836ba009..5e5f5cf9ad 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -453,6 +453,12 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, c = 1; *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; break; + case QCOW2_CLUSTER_ZERO: + c = count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], 0, + QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); + *cluster_offset = 0; + break; case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); @@ -461,7 +467,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ c = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, QCOW_OFLAG_COMPRESSED); + &l2_table[l2_index], 0, + QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); *cluster_offset &= L2E_OFFSET_MASK; break; } @@ -720,6 +727,7 @@ static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, break; case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_CLUSTER_ZERO: break; default: abort(); @@ -868,9 +876,10 @@ again: && (cluster_offset & QCOW_OFLAG_COPIED)) { /* We keep all QCOW_OFLAG_COPIED clusters */ - keep_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size, - &l2_table[l2_index], 0, - QCOW_OFLAG_COPIED); + keep_clusters = + count_contiguous_clusters(nb_clusters, s->cluster_size, + &l2_table[l2_index], 0, + QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); nb_clusters -= keep_clusters; } else { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 0112cc37b0..812c93c5c7 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -703,6 +703,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, nb_clusters << s->cluster_bits); break; case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_ZERO: break; default: abort(); @@ -973,6 +974,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_entry & ~511, nb_csectors * 512); break; + case QCOW2_CLUSTER_ZERO: + if ((l2_entry & L2E_OFFSET_MASK) == 0) { + break; + } + /* fall through */ + case QCOW2_CLUSTER_NORMAL: { /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ diff --git a/block/qcow2.c b/block/qcow2.c index 3e1482fa7e..9a8b354d0e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -536,6 +536,14 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, } break; + case QCOW2_CLUSTER_ZERO: + if (s->qcow_version < 3) { + ret = -EIO; + goto fail; + } + qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors); + break; + case QCOW2_CLUSTER_COMPRESSED: /* add AIO support for compressed blocks ? */ ret = qcow2_decompress_cluster(bs, cluster_offset); diff --git a/block/qcow2.h b/block/qcow2.h index 2dc8ca26fb..df2bdfd0c6 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -43,6 +43,8 @@ #define QCOW_OFLAG_COPIED (1LL << 63) /* indicate that the cluster is compressed (they never have the copied flag) */ #define QCOW_OFLAG_COMPRESSED (1LL << 62) +/* The cluster reads as all zeros */ +#define QCOW_OFLAG_ZERO (1LL << 0) #define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */ @@ -184,6 +186,7 @@ enum { QCOW2_CLUSTER_UNALLOCATED, QCOW2_CLUSTER_NORMAL, QCOW2_CLUSTER_COMPRESSED, + QCOW2_CLUSTER_ZERO }; #define L1E_OFFSET_MASK 0x00ffffffffffff00ULL @@ -213,6 +216,8 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { return QCOW2_CLUSTER_COMPRESSED; + } else if (l2_entry & QCOW_OFLAG_ZERO) { + return QCOW2_CLUSTER_ZERO; } else if (!(l2_entry & L2E_OFFSET_MASK)) { return QCOW2_CLUSTER_UNALLOCATED; } else { From cfcc4c62ffe37170cf4b0c9e7acbd2dbaa792598 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2012 15:20:27 +0200 Subject: [PATCH 33/38] qcow2: Support for feature table header extension Instead of printing an ugly bitmask, qemu can now print a more helpful string even for yet unknown features. Signed-off-by: Kevin Wolf --- block/qcow2.c | 66 ++++++++++++++++++++++++++++++++++++++++++++------- block/qcow2.h | 12 ++++++++++ 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 9a8b354d0e..41b195d91b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -54,6 +54,7 @@ typedef struct { } QCowExtension; #define QCOW2_EXT_MAGIC_END 0 #define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA +#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -76,7 +77,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) * return 0 upon success, non-0 otherwise */ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset) + uint64_t end_offset, void **p_feature_table) { BDRVQcowState *s = bs->opaque; QCowExtension ext; @@ -134,6 +135,18 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, #endif break; + case QCOW2_EXT_MAGIC_FEATURE_TABLE: + if (p_feature_table != NULL) { + void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); + ret = bdrv_pread(bs->file, offset , feature_table, ext.len); + if (ret < 0) { + return ret; + } + + *p_feature_table = feature_table; + } + break; + default: /* unknown magic - save it in case we need to rewrite the header */ { @@ -182,6 +195,24 @@ static void report_unsupported(BlockDriverState *bs, const char *fmt, ...) bs->device_name, "qcow2", msg); } +static void report_unsupported_feature(BlockDriverState *bs, + Qcow2Feature *table, uint64_t mask) +{ + while (table && table->name[0] != '\0') { + if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { + if (mask & (1 << table->bit)) { + report_unsupported(bs, "%.46s",table->name); + mask &= ~(1 << table->bit); + } + } + table++; + } + + if (mask) { + report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask); + } +} + static int qcow2_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; @@ -245,14 +276,23 @@ static int qcow2_open(BlockDriverState *bs, int flags) } } + if (header.backing_file_offset) { + ext_end = header.backing_file_offset; + } else { + ext_end = 1 << header.cluster_bits; + } + /* Handle feature bits */ s->incompatible_features = header.incompatible_features; s->compatible_features = header.compatible_features; s->autoclear_features = header.autoclear_features; if (s->incompatible_features != 0) { - report_unsupported(bs, "incompatible features mask %" PRIx64, - header.incompatible_features); + void *feature_table = NULL; + qcow2_read_extensions(bs, header.header_length, ext_end, + &feature_table); + report_unsupported_feature(bs, feature_table, + s->incompatible_features); ret = -ENOTSUP; goto fail; } @@ -343,12 +383,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) QLIST_INIT(&s->cluster_allocs); /* read qcow2 extensions */ - if (header.backing_file_offset) { - ext_end = header.backing_file_offset; - } else { - ext_end = s->cluster_size; - } - if (qcow2_read_extensions(bs, header.header_length, ext_end)) { + if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) { ret = -EINVAL; goto fail; } @@ -912,6 +947,19 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } + /* Feature table */ + Qcow2Feature features[] = { + /* no feature defined yet */ + }; + + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, + features, sizeof(features), buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; + /* Keep unknown header extensions */ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); diff --git a/block/qcow2.h b/block/qcow2.h index df2bdfd0c6..2a982445a1 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -104,6 +104,18 @@ typedef struct Qcow2UnknownHeaderExtension { uint8_t data[]; } Qcow2UnknownHeaderExtension; +enum { + QCOW2_FEAT_TYPE_INCOMPATIBLE = 0, + QCOW2_FEAT_TYPE_COMPATIBLE = 1, + QCOW2_FEAT_TYPE_AUTOCLEAR = 2, +}; + +typedef struct Qcow2Feature { + uint8_t type; + uint8_t bit; + char name[46]; +} QEMU_PACKED Qcow2Feature; + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; From ee3a315edfb12d3c18195410050eed0e2d49cc27 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Mar 2012 18:15:01 +0100 Subject: [PATCH 34/38] qemu-iotests: add a simple test for write_zeroes Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- tests/qemu-iotests/033 | 73 ++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/033.out | 29 +++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 103 insertions(+) create mode 100755 tests/qemu-iotests/033 create mode 100644 tests/qemu-iotests/033.out diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 new file mode 100755 index 0000000000..9aee0784f6 --- /dev/null +++ b/tests/qemu-iotests/033 @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Test aligned and misaligned write zeroes operations. +# +# Copyright (C) 2012 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=pbonzini@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + + +size=128M +_make_test_img $size + +echo +echo "== preparing image ==" +$QEMU_IO -c "write -P 0xa 0x200 0x400" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xa 0x20000 0x600" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 0x400 0x20000" $TEST_IMG | _filter_qemu_io + +echo +echo "== verifying patterns (1) ==" +$QEMU_IO -c "read -P 0xa 0x200 0x200" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 0x20400 0x200" $TEST_IMG | _filter_qemu_io + +echo +echo "== rewriting zeroes ==" +$QEMU_IO -c "write -P 0xb 0x10000 0x10000" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 0x10000 0x10000" $TEST_IMG | _filter_qemu_io + +echo +echo "== verifying patterns (2) ==" +$QEMU_IO -c "read -P 0x0 0x400 0x20000" $TEST_IMG | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/033.out b/tests/qemu-iotests/033.out new file mode 100644 index 0000000000..89348831db --- /dev/null +++ b/tests/qemu-iotests/033.out @@ -0,0 +1,29 @@ +QA output created by 033 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +== preparing image == +wrote 1024/1024 bytes at offset 512 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1536/1536 bytes at offset 131072 +2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (1) == +read 512/512 bytes at offset 512 +512.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 132096 +512.000000 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rewriting zeroes == +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verifying patterns (2) == +read 131072/131072 bytes at offset 1024 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 92508a748e..af34482c79 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -39,3 +39,4 @@ 030 rw auto 031 rw auto quick 032 rw auto +033 rw auto From ab3a32ad5ea841c313e5f9c5f0d4ec02a8e9ddfc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2012 15:28:12 +0200 Subject: [PATCH 35/38] qemu-iotests: Test backing file COW with zero clusters Signed-off-by: Kevin Wolf --- tests/qemu-iotests/034 | 113 +++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/034.out | 81 ++++++++++++++++++++++++++ tests/qemu-iotests/group | 1 + 3 files changed, 195 insertions(+) create mode 100755 tests/qemu-iotests/034 create mode 100644 tests/qemu-iotests/034.out diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 new file mode 100755 index 0000000000..8254df82ba --- /dev/null +++ b/tests/qemu-iotests/034 @@ -0,0 +1,113 @@ +#!/bin/bash +# +# Test bdrv_write_zeroes with backing files +# +# Copyright (C) 2012 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow qcow2 vmdk qed +_supported_proto generic +_supported_os Linux + +CLUSTER_SIZE=4k +size=128M + +echo +echo "== creating backing file for COW tests ==" + +_make_test_img $size +$QEMU_IO -c "write -P 0x55 0 1M" $TEST_IMG | _filter_qemu_io +mv $TEST_IMG $TEST_IMG.base + +_make_test_img -b $TEST_IMG.base 6G + +echo +echo "== zero write with backing file ==" +$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -z 513k 13k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (3) ==" +$QEMU_IO -c "read -P 0x55 0 64k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 256k 257k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io + +echo +echo "== overwriting zero cluster ==" +$QEMU_IO -c "write -P 0xa 60k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xd 252k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "write -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (4) ==" +$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xb 64k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 72k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xc 76k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 80k 168k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xe 248k 8k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 64k" $TEST_IMG | _filter_qemu_io + +echo +echo "== re-zeroing overwritten area ==" +$QEMU_IO -c "write -z 64k 192k" $TEST_IMG | _filter_qemu_io + +_check_test_img + +echo +echo "== verifying patterns (5) ==" +$QEMU_IO -c "read -P 0x55 0 60k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xa 60k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 64k 192k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0xd 256k 4k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 260k 253k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x0 513k 13k" $TEST_IMG | _filter_qemu_io +$QEMU_IO -c "read -P 0x55 526k 498k" $TEST_IMG | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/034.out b/tests/qemu-iotests/034.out new file mode 100644 index 0000000000..e82dae55f7 --- /dev/null +++ b/tests/qemu-iotests/034.out @@ -0,0 +1,81 @@ +QA output created by 034 + +== creating backing file for COW tests == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' + +== zero write with backing file == +wrote 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (3) == +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 263168/263168 bytes at offset 262144 +257 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 509952/509952 bytes at offset 538624 +498 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== overwriting zero cluster == +wrote 8192/8192 bytes at offset 61440 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 65536 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4096/4096 bytes at offset 77824 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 258048 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 8192/8192 bytes at offset 253952 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (4) == +read 61440/61440 bytes at offset 0 +60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 61440 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 8192/8192 bytes at offset 65536 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 73728 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 77824 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 172032/172032 bytes at offset 81920 +168 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 8192/8192 bytes at offset 253952 +8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 262144 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 266240 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== re-zeroing overwritten area == +wrote 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +== verifying patterns (5) == +read 61440/61440 bytes at offset 0 +60 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 61440 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 196608/196608 bytes at offset 65536 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 262144 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 259072/259072 bytes at offset 266240 +253 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 13312/13312 bytes at offset 525312 +13 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 509952/509952 bytes at offset 538624 +498 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index af34482c79..5934829d3f 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -40,3 +40,4 @@ 031 rw auto quick 032 rw auto 033 rw auto +034 rw auto backing From 621f058940ea9f1ef3d8774ef3203544f1228df1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 20 Mar 2012 15:12:58 +0100 Subject: [PATCH 36/38] qcow2: Zero write support Signed-off-by: Kevin Wolf --- block.c | 14 +++++++-- block/qcow2-cluster.c | 72 +++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 21 +++++++++++++ block/qcow2.h | 1 + 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index 421cd1bebd..fe74dddb13 100644 --- a/block.c +++ b/block.c @@ -80,6 +80,8 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, void *opaque, bool is_write); static void coroutine_fn bdrv_co_do_rw(void *opaque); +static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors); static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors, bool is_write, double elapsed_time, uint64_t *wait); @@ -1708,8 +1710,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, if (drv->bdrv_co_write_zeroes && buffer_is_zero(bounce_buffer, iov.iov_len)) { - ret = drv->bdrv_co_write_zeroes(bs, cluster_sector_num, - cluster_nb_sectors); + ret = bdrv_co_do_write_zeroes(bs, cluster_sector_num, + cluster_nb_sectors); } else { ret = drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors, &bounce_qiov); @@ -1819,9 +1821,15 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, struct iovec iov; int ret; + /* TODO Emulate only part of misaligned requests instead of letting block + * drivers return -ENOTSUP and emulate everything */ + /* First try the efficient write zeroes operation */ if (drv->bdrv_co_write_zeroes) { - return drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors); + ret = drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors); + if (ret != -ENOTSUP) { + return ret; + } } /* Fall back to bounce buffer if write zeroes is unsupported */ diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 5e5f5cf9ad..a747a88e13 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1102,3 +1102,75 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, return 0; } + +/* + * This zeroes as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of zeroed + * clusters. + */ +static int zero_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + + /* Update L2 entries */ + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + if (old_offset & QCOW_OFLAG_COMPRESSED) { + l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); + qcow2_free_any_clusters(bs, old_offset, 1); + } else { + l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); + } + } + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + + return nb_clusters; +} + +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + unsigned int nb_clusters; + int ret; + + /* The zero flag is only supported by version 3 and newer */ + if (s->qcow_version < 3) { + return -ENOTSUP; + } + + /* Each L2 table is handled by its own loop iteration */ + nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS); + + while (nb_clusters > 0) { + ret = zero_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + return ret; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + return 0; +} diff --git a/block/qcow2.c b/block/qcow2.c index 41b195d91b..ad46c03c2f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1281,6 +1281,26 @@ static int qcow2_make_empty(BlockDriverState *bs) return 0; } +static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) +{ + int ret; + BDRVQcowState *s = bs->opaque; + + /* Emulate misaligned zero writes */ + if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) { + return -ENOTSUP; + } + + /* Whatever is left can use real zero clusters */ + qemu_co_mutex_lock(&s->lock); + ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { @@ -1558,6 +1578,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_co_writev = qcow2_co_writev, .bdrv_co_flush_to_os = qcow2_co_flush_to_os, + .bdrv_co_write_zeroes = qcow2_co_write_zeroes, .bdrv_co_discard = qcow2_co_discard, .bdrv_truncate = qcow2_truncate, .bdrv_write_compressed = qcow2_write_compressed, diff --git a/block/qcow2.h b/block/qcow2.h index 2a982445a1..93567f6451 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -283,6 +283,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); From 8900436891cd8955b0795945aa020c04eaa0e5d2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 27 Mar 2012 13:45:14 +0200 Subject: [PATCH 37/38] qemu-iotests: Add -o and make v3 the default for qcow2 This adds an -o option to qemu-iotests, which is an option string that is passed through to qemu-img create -o... This allows testing different subformat with a command like './check -qcow2 -o compat=0.10'. For qcow2, if no compat option is specified, compat=1.1 is the new default. Signed-off-by: Kevin Wolf --- tests/qemu-iotests/check | 6 +++--- tests/qemu-iotests/common | 17 +++++++++++++++++ tests/qemu-iotests/common.rc | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index aae1378998..432732cfc3 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -41,9 +41,6 @@ then exit 1 fi -# we need common -. ./common - # we need common.rc if ! . ./common.rc then @@ -51,6 +48,9 @@ then exit 1 fi +# we need common +. ./common + #if [ `id -u` -ne 0 ] #then # echo "check: QA must be run as root" diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index c187f6c573..eeb70cbcdc 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -35,6 +35,7 @@ diff="diff -u" verbose=false group=false xgroup=false +imgopts=false showme=false sortme=false expunge=true @@ -44,6 +45,7 @@ rm -f $tmp.list $tmp.tmp $tmp.sed export IMGFMT=raw export IMGPROTO=file +export IMGOPTS="" export QEMU_IO_OPTIONS="" for r @@ -103,6 +105,13 @@ s/ .*//p mv $tmp.tmp $tmp.list xgroup=false continue + + elif $imgopts + then + IMGOPTS="$r" + imgopts=false + continue + fi xpand=true @@ -130,6 +139,7 @@ check options -nocache use O_DIRECT on backing file -misalign misalign memory allocations -n show me, do not run tests + -o options -o options to pass to qemu-img create/convert -T output timestamps -r randomize test order @@ -223,6 +233,10 @@ testlist options showme=true xpand=false ;; + -o) + imgopts=true + xpand=false + ;; -r) # randomize test order randomize=true xpand=false @@ -299,6 +313,9 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ done +# Set default options for qemu-img create -o if they were not specified +_set_default_imgopts + if [ -s $tmp.list ] then # found some valid test numbers ... this is good diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 00ee754c8d..4bc7420ece 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -53,19 +53,44 @@ else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi +_optstr_add() +{ + if [ -n "$1" ]; then + echo "$1,$2" + else + echo "$2" + fi +} + +_set_default_imgopts() +{ + if [ "$IMGFMT" == "qcow2" ] && ! (echo "$IMGOPTS" | grep "compat=" > /dev/null); then + IMGOPTS=$(_optstr_add "$IMGOPTS" "compat=1.1") + fi +} + _make_test_img() { # extra qemu-img options can be added by tests # at least one argument (the image size) needs to be added local extra_img_options="" local image_size=$* + local optstr="" + + if [ -n "$IMGOPTS" ]; then + optstr=$(_optstr_add "$optstr" "$IMGOPTS") + fi if [ "$1" = "-b" ]; then extra_img_options="$1 $2" image_size=$3 fi if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then - extra_img_options="-o cluster_size=$CLUSTER_SIZE $extra_img_options" + optstr=$(_optstr_add "$optstr" "cluster_size=$CLUSTER_SIZE") + fi + + if [ -n "$optstr" ]; then + extra_img_options="-o $optstr $extra_img_options" fi # XXX(hch): have global image options? @@ -76,6 +101,7 @@ _make_test_img() sed -e "s# encryption=off##g" | \ sed -e "s# cluster_size=[0-9]\\+##g" | \ sed -e "s# table_size=0##g" | \ + sed -e "s# compat='[^']*'##g" | \ sed -e "s# compat6=off##g" | \ sed -e "s# static=off##g" } @@ -268,7 +294,11 @@ _require_command() _full_imgfmt_details() { - echo "$IMGFMT" + if [ -n "$IMGOPTS" ]; then + echo "$IMGFMT ($IMGOPTS)" + else + echo "$IMGFMT" + fi } _full_imgproto_details() From 1042ec94b19e0bfaae74c912ebbdfdbff8dd7db2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 12 Apr 2012 17:21:44 +0200 Subject: [PATCH 38/38] qemu-iotests: Fix test 031 for qcow2 v3 support qcow2.py must be updated to work with version 3 images at all, the output has changed since the feature table extension has been added, and version 2 and version 3 images can't possibly have the same test output. Change the test case to completely ignore IMGOPTS and run the test for both compat=1.1 and compat=0.10 regardless of the ./check command line. Signed-off-by: Kevin Wolf --- tests/qemu-iotests/031 | 44 +++++++----- tests/qemu-iotests/031.out | 130 +++++++++++++++++++++++++++++++++++- tests/qemu-iotests/qcow2.py | 24 +++++-- 3 files changed, 172 insertions(+), 26 deletions(-) diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 6365f287e0..2d5e3b12d1 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -45,26 +45,34 @@ _supported_proto generic _supported_os Linux CLUSTER_SIZE=65536 -echo -echo === Create image with unknown header extension === -echo -_make_test_img 64M -./qcow2.py $TEST_IMG add-header-ext 0x12345678 "This is a test header extension" -./qcow2.py $TEST_IMG dump-header -_check_test_img -echo -echo === Rewrite header with no backing file === -echo -$QEMU_IMG rebase -u -b "" $TEST_IMG -./qcow2.py $TEST_IMG dump-header -_check_test_img +# qcow2.py output depends on the exact options used, so override the command +# line here as an exception +for IMGOPTS in "compat=0.10" "compat=1.1"; do -echo -echo === Add a backing file and format === -echo -$QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device $TEST_IMG -./qcow2.py $TEST_IMG dump-header + echo + echo ===== Testing with -o $IMGOPTS ===== + echo + echo === Create image with unknown header extension === + echo + _make_test_img 64M + ./qcow2.py $TEST_IMG add-header-ext 0x12345678 "This is a test header extension" + ./qcow2.py $TEST_IMG dump-header + _check_test_img + + echo + echo === Rewrite header with no backing file === + echo + $QEMU_IMG rebase -u -b "" $TEST_IMG + ./qcow2.py $TEST_IMG dump-header + _check_test_img + + echo + echo === Add a backing file and format === + echo + $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device $TEST_IMG + ./qcow2.py $TEST_IMG dump-header +done # success, all done echo "*** done" diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index e1a536ec00..d3cab301d4 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -1,5 +1,7 @@ QA output created by 031 +===== Testing with -o compat=0.10 ===== + === Create image with unknown header extension === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -16,6 +18,11 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 Header extension: magic 0x12345678 @@ -39,6 +46,16 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 + +Header extension: +magic 0x6803f857 +length 0 +data '' Header extension: magic 0x12345678 @@ -51,7 +68,7 @@ No errors were found on the image. magic 0x514649fb version 2 -backing_file_offset 0x90 +backing_file_offset 0x98 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -62,12 +79,123 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 72 Header extension: magic 0xe2792aca length 11 data 'host_device' +Header extension: +magic 0x6803f857 +length 0 +data '' + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + + +===== Testing with -o compat=1.1 ===== + +=== Create image with unknown header extension === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + +No errors were found on the image. + +=== Rewrite header with no backing file === + +magic 0x514649fb +version 3 +backing_file_offset 0x0 +backing_file_size 0x0 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0x6803f857 +length 0 +data '' + +Header extension: +magic 0x12345678 +length 31 +data 'This is a test header extension' + +No errors were found on the image. + +=== Add a backing file and format === + +magic 0x514649fb +version 3 +backing_file_offset 0xb8 +backing_file_size 0x17 +cluster_bits 16 +size 67108864 +crypt_method 0 +l1_size 1 +l1_table_offset 0x30000 +refcount_table_offset 0x10000 +refcount_table_clusters 1 +nb_snapshots 0 +snapshot_offset 0x0 +incompatible_features 0x0 +compatible_features 0x0 +autoclear_features 0x0 +refcount_order 4 +header_length 104 + +Header extension: +magic 0xe2792aca +length 11 +data 'host_device' + +Header extension: +magic 0x6803f857 +length 0 +data '' + Header extension: magic 0x12345678 length 31 diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index bfb47e88fc..e27196aa26 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -35,6 +35,13 @@ class QcowHeader: [ uint32_t, '%d', 'refcount_table_clusters' ], [ uint32_t, '%d', 'nb_snapshots' ], [ uint64_t, '%#x', 'snapshot_offset' ], + + # Version 3 header fields + [ uint64_t, '%#x', 'incompatible_features' ], + [ uint64_t, '%#x', 'compatible_features' ], + [ uint64_t, '%#x', 'autoclear_features' ], + [ uint32_t, '%d', 'refcount_order' ], + [ uint32_t, '%d', 'header_length' ], ]; fmt = '>' + ''.join(field[0] for field in fields) @@ -50,9 +57,10 @@ class QcowHeader: self.__dict__ = dict((field[2], header[i]) for i, field in enumerate(QcowHeader.fields)) + self.set_defaults() self.cluster_size = 1 << self.cluster_bits - fd.seek(self.get_header_length()) + fd.seek(self.header_length) self.load_extensions(fd) if self.backing_file_offset: @@ -61,11 +69,13 @@ class QcowHeader: else: self.backing_file = None - def get_header_length(self): + def set_defaults(self): if self.version == 2: - return 72 - else: - raise Exception("version != 2 not supported") + self.incompatible_features = 0 + self.compatible_features = 0 + self.autoclear_features = 0 + self.refcount_order = 4 + self.header_length = 72 def load_extensions(self, fd): self.extensions = [] @@ -86,7 +96,7 @@ class QcowHeader: def update_extensions(self, fd): - fd.seek(self.get_header_length()) + fd.seek(self.header_length) extensions = self.extensions extensions.append(QcowHeaderExtension(0, 0, "")) for ex in extensions: @@ -103,7 +113,7 @@ class QcowHeader: def update(self, fd): - header_bytes = self.get_header_length() + header_bytes = self.header_length self.update_extensions(fd)