1 | The following changes since commit 9436e082de18b2fb2ceed2e9d1beef641ae64f23: | 1 | The following changes since commit 1b8c45899715d292398152ba97ef755ccaf84680: |
---|---|---|---|
2 | 2 | ||
3 | MAINTAINERS: clarify some of the tags (2018-11-19 11:19:23 +0000) | 3 | Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20200507a' into staging (2020-05-07 18:43:20 +0100) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 6d0a4a0fb5c8f10c8eb68b52cfda0082b00ae963: | 9 | for you to fetch changes up to 47e0b38a13935cb666f88964c3096654092f42d6: |
10 | 10 | ||
11 | iotests: Test file-posix locking and reopen (2018-11-19 14:32:04 +0100) | 11 | block: Drop unused .bdrv_has_zero_init_truncate (2020-05-08 13:26:35 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches: | 14 | Block layer patches: |
15 | 15 | ||
16 | - file-posix: Fix shared permission locks after reopen | 16 | - qcow2: Fix preallocation on block devices |
17 | - block: Fix error path for failed .bdrv_reopen_prepare | 17 | - backup: Make sure that source and target size match |
18 | - qcow2: Catch invalid allocations when the image becomes too large | 18 | - vmdk: Fix zero cluster handling |
19 | - vvfat/fdc/nvme: Fix segfaults and leaks | 19 | - Follow-up cleanups and fixes for the truncate changes |
20 | - iotests: Skip more tests if required drivers are missing | ||
20 | 21 | ||
21 | ---------------------------------------------------------------- | 22 | ---------------------------------------------------------------- |
22 | Eric Blake (3): | 23 | Alberto Garcia (1): |
23 | qcow2: Document some maximum size constraints | 24 | qcow2: Avoid integer wraparound in qcow2_co_truncate() |
24 | qcow2: Don't allow overflow during cluster allocation | ||
25 | iotests: Add new test 220 for max compressed cluster offset | ||
26 | 25 | ||
27 | Kevin Wolf (1): | 26 | Eric Blake (9): |
28 | vvfat: Fix memory leak | 27 | gluster: Drop useless has_zero_init callback |
28 | file-win32: Support BDRV_REQ_ZERO_WRITE for truncate | ||
29 | nfs: Support BDRV_REQ_ZERO_WRITE for truncate | ||
30 | rbd: Support BDRV_REQ_ZERO_WRITE for truncate | ||
31 | sheepdog: Support BDRV_REQ_ZERO_WRITE for truncate | ||
32 | ssh: Support BDRV_REQ_ZERO_WRITE for truncate | ||
33 | parallels: Rework truncation logic | ||
34 | vhdx: Rework truncation logic | ||
35 | block: Drop unused .bdrv_has_zero_init_truncate | ||
29 | 36 | ||
30 | Li Qiang (1): | 37 | Kevin Wolf (11): |
31 | nvme: fix oob access issue(CVE-2018-16847) | 38 | vmdk: Rename VmdkMetaData.valid to new_allocation |
39 | vmdk: Fix zero cluster allocation | ||
40 | vmdk: Fix partial overwrite of zero cluster | ||
41 | vmdk: Don't update L2 table for zero write on zero cluster | ||
42 | vmdk: Flush only once in vmdk_L2update() | ||
43 | iotests: vmdk: Enable zeroed_grained=on by default | ||
44 | iotests/283: Use consistent size for source and target | ||
45 | backup: Improve error for bdrv_getlength() failure | ||
46 | backup: Make sure that source and target size match | ||
47 | iotests: Backup with different source/target size | ||
48 | iotests/055: Use cache.no-flush for vmdk target | ||
32 | 49 | ||
33 | Mark Cave-Ayland (1): | 50 | Max Reitz (1): |
34 | fdc: fix segfault in fdctrl_stop_transfer() when DMA is disabled | 51 | qcow2: Fix preallocation on block devices |
35 | 52 | ||
36 | Max Reitz (3): | 53 | Vladimir Sementsov-Ogievskiy (8): |
37 | block: Always abort reopen after prepare succeeded | 54 | iotests: handle tmpfs |
38 | file-posix: Fix shared locks on reopen commit | 55 | iotests/082: require bochs |
39 | iotests: Test file-posix locking and reopen | 56 | iotests/148: use skip_if_unsupported |
57 | iotests/041: drop self.assert_no_active_block_jobs() | ||
58 | iotests/055: refactor compressed backup to vmdk | ||
59 | iotests/055: skip vmdk target tests if vmdk is not whitelisted | ||
60 | iotests/109: mark required formats as required to support whitelisting | ||
61 | iotests/113: mark bochs as required to support whitelisting | ||
40 | 62 | ||
41 | docs/interop/qcow2.txt | 38 +++++++++++++++++- | 63 | include/block/block.h | 1 - |
42 | block/qcow2.h | 6 +++ | 64 | include/block/block_int.h | 7 --- |
43 | block.c | 12 ++++++ | 65 | block.c | 21 -------- |
44 | block/file-posix.c | 2 +- | 66 | block/backup-top.c | 14 +++-- |
45 | block/qcow2-refcount.c | 20 ++++++---- | 67 | block/backup.c | 18 +++++-- |
46 | block/vvfat.c | 6 +-- | 68 | block/file-posix.c | 1 - |
47 | hw/block/fdc.c | 2 +- | 69 | block/file-win32.c | 4 +- |
48 | hw/block/nvme.c | 7 ++++ | 70 | block/gluster.c | 14 ----- |
49 | tests/qemu-iotests/182 | 71 ++++++++++++++++++++++++++++++++++ | 71 | block/nfs.c | 4 +- |
50 | tests/qemu-iotests/182.out | 9 +++++ | 72 | block/parallels.c | 25 +++++---- |
51 | tests/qemu-iotests/220 | 96 ++++++++++++++++++++++++++++++++++++++++++++++ | 73 | block/qcow2.c | 23 ++++++--- |
52 | tests/qemu-iotests/220.out | 54 ++++++++++++++++++++++++++ | 74 | block/qed.c | 1 - |
53 | tests/qemu-iotests/group | 1 + | 75 | block/raw-format.c | 6 --- |
54 | 13 files changed, 310 insertions(+), 14 deletions(-) | 76 | block/rbd.c | 4 +- |
55 | create mode 100755 tests/qemu-iotests/220 | 77 | block/sheepdog.c | 4 +- |
56 | create mode 100644 tests/qemu-iotests/220.out | 78 | block/ssh.c | 5 +- |
79 | block/vhdx.c | 89 ++++++++++++++++++-------------- | ||
80 | block/vmdk.c | 47 ++++++++++------- | ||
81 | tests/qemu-iotests/041 | 8 --- | ||
82 | tests/qemu-iotests/055 | 120 ++++++++++++++++++++++++++++++------------- | ||
83 | tests/qemu-iotests/055.out | 4 +- | ||
84 | tests/qemu-iotests/059 | 6 +-- | ||
85 | tests/qemu-iotests/082 | 1 + | ||
86 | tests/qemu-iotests/091 | 2 +- | ||
87 | tests/qemu-iotests/109 | 1 + | ||
88 | tests/qemu-iotests/113 | 4 +- | ||
89 | tests/qemu-iotests/148 | 1 + | ||
90 | tests/qemu-iotests/283 | 6 ++- | ||
91 | tests/qemu-iotests/283.out | 2 +- | ||
92 | tests/qemu-iotests/292 | 73 ++++++++++++++++++++++++++ | ||
93 | tests/qemu-iotests/292.out | 24 +++++++++ | ||
94 | tests/qemu-iotests/check | 3 ++ | ||
95 | tests/qemu-iotests/common.rc | 37 ++++++++++++- | ||
96 | tests/qemu-iotests/group | 1 + | ||
97 | 34 files changed, 386 insertions(+), 195 deletions(-) | ||
98 | create mode 100755 tests/qemu-iotests/292 | ||
99 | create mode 100644 tests/qemu-iotests/292.out | ||
57 | 100 | ||
101 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Some tests requires O_DIRECT, or want it by default. Introduce smarter | ||
4 | O_DIRECT handling: | ||
5 | |||
6 | - Check O_DIRECT in common.rc, if it is requested by selected | ||
7 | cache-mode. | ||
8 | |||
9 | - Support second fall-through argument in _default_cache_mode | ||
10 | |||
11 | Inspired-by: Max's 23e1d054112cec1e | ||
12 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Message-Id: <20200430124713.3067-2-vsementsov@virtuozzo.com> | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | --- | ||
16 | tests/qemu-iotests/091 | 2 +- | ||
17 | tests/qemu-iotests/common.rc | 37 ++++++++++++++++++++++++++++++++++-- | ||
18 | 2 files changed, 36 insertions(+), 3 deletions(-) | ||
19 | |||
20 | diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 | ||
21 | index XXXXXXX..XXXXXXX 100755 | ||
22 | --- a/tests/qemu-iotests/091 | ||
23 | +++ b/tests/qemu-iotests/091 | ||
24 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
25 | _supported_fmt qcow2 | ||
26 | _supported_proto file | ||
27 | _supported_os Linux | ||
28 | -_default_cache_mode none | ||
29 | _supported_cache_modes writethrough none writeback | ||
30 | +_default_cache_mode none writeback | ||
31 | |||
32 | size=1G | ||
33 | |||
34 | diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc | ||
35 | index XXXXXXX..XXXXXXX 100644 | ||
36 | --- a/tests/qemu-iotests/common.rc | ||
37 | +++ b/tests/qemu-iotests/common.rc | ||
38 | @@ -XXX,XX +XXX,XX @@ _supported_cache_modes() | ||
39 | _notrun "not suitable for cache mode: $CACHEMODE" | ||
40 | } | ||
41 | |||
42 | +# Check whether the filesystem supports O_DIRECT | ||
43 | +_check_o_direct() | ||
44 | +{ | ||
45 | + $QEMU_IMG create -f raw "$TEST_IMG".test_o_direct 1M > /dev/null | ||
46 | + out=$($QEMU_IO -f raw -t none -c quit "$TEST_IMG".test_o_direct 2>&1) | ||
47 | + rm -f "$TEST_IMG".test_o_direct | ||
48 | + | ||
49 | + [[ "$out" != *"O_DIRECT"* ]] | ||
50 | +} | ||
51 | + | ||
52 | +_require_o_direct() | ||
53 | +{ | ||
54 | + if ! _check_o_direct; then | ||
55 | + _notrun "file system on $TEST_DIR does not support O_DIRECT" | ||
56 | + fi | ||
57 | +} | ||
58 | + | ||
59 | +_check_cache_mode() | ||
60 | +{ | ||
61 | + if [ $CACHEMODE == "none" ] || [ $CACHEMODE == "directsync" ]; then | ||
62 | + _require_o_direct | ||
63 | + fi | ||
64 | +} | ||
65 | + | ||
66 | +_check_cache_mode | ||
67 | + | ||
68 | +# $1 - cache mode to use by default | ||
69 | +# $2 - (optional) cache mode to use by default if O_DIRECT is not supported | ||
70 | _default_cache_mode() | ||
71 | { | ||
72 | if $CACHEMODE_IS_DEFAULT; then | ||
73 | - CACHEMODE="$1" | ||
74 | - QEMU_IO="$QEMU_IO --cache $1" | ||
75 | + if [ -z "$2" ] || _check_o_direct; then | ||
76 | + CACHEMODE="$1" | ||
77 | + else | ||
78 | + CACHEMODE="$2" | ||
79 | + fi | ||
80 | + QEMU_IO="$QEMU_IO --cache $CACHEMODE" | ||
81 | + _check_cache_mode | ||
82 | return | ||
83 | fi | ||
84 | } | ||
85 | -- | ||
86 | 2.25.3 | ||
87 | |||
88 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Test fails if bochs not whitelisted, so, skip it in this case. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Message-Id: <20200430124713.3067-3-vsementsov@virtuozzo.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | tests/qemu-iotests/082 | 1 + | ||
10 | 1 file changed, 1 insertion(+) | ||
11 | |||
12 | diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 | ||
13 | index XXXXXXX..XXXXXXX 100755 | ||
14 | --- a/tests/qemu-iotests/082 | ||
15 | +++ b/tests/qemu-iotests/082 | ||
16 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
17 | |||
18 | _supported_fmt qcow2 | ||
19 | _supported_proto file nfs | ||
20 | +_require_drivers bochs | ||
21 | |||
22 | run_qemu_img() | ||
23 | { | ||
24 | -- | ||
25 | 2.25.3 | ||
26 | |||
27 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Skip test-case with quorum if quorum is not whitelisted. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Message-Id: <20200430124713.3067-4-vsementsov@virtuozzo.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | tests/qemu-iotests/148 | 1 + | ||
10 | 1 file changed, 1 insertion(+) | ||
11 | |||
12 | diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 | ||
13 | index XXXXXXX..XXXXXXX 100755 | ||
14 | --- a/tests/qemu-iotests/148 | ||
15 | +++ b/tests/qemu-iotests/148 | ||
16 | @@ -XXX,XX +XXX,XX @@ sector = "%d" | ||
17 | ''' % bad_sector) | ||
18 | file.close() | ||
19 | |||
20 | + @iotests.skip_if_unsupported(['quorum']) | ||
21 | def setUp(self): | ||
22 | driveopts = ['driver=quorum', 'vote-threshold=2'] | ||
23 | driveopts.append('read-pattern=%s' % self.read_pattern) | ||
24 | -- | ||
25 | 2.25.3 | ||
26 | |||
27 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Drop check for no block-jobs: it's obvious that there no jobs | ||
4 | immediately after vm.launch(). | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Message-Id: <20200430124713.3067-5-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | tests/qemu-iotests/041 | 8 -------- | ||
11 | 1 file changed, 8 deletions(-) | ||
12 | |||
13 | diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 | ||
14 | index XXXXXXX..XXXXXXX 100755 | ||
15 | --- a/tests/qemu-iotests/041 | ||
16 | +++ b/tests/qemu-iotests/041 | ||
17 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
18 | pass | ||
19 | |||
20 | def test_complete(self): | ||
21 | - self.assert_no_active_block_jobs() | ||
22 | - | ||
23 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
24 | sync='full', node_name="repair0", replaces="img1", | ||
25 | target=quorum_repair_img, format=iotests.imgfmt) | ||
26 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
27 | 'target image does not match source after mirroring') | ||
28 | |||
29 | def test_cancel(self): | ||
30 | - self.assert_no_active_block_jobs() | ||
31 | - | ||
32 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
33 | sync='full', node_name="repair0", replaces="img1", | ||
34 | target=quorum_repair_img, format=iotests.imgfmt) | ||
35 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
36 | self.assert_has_block_node(None, quorum_img3) | ||
37 | |||
38 | def test_cancel_after_ready(self): | ||
39 | - self.assert_no_active_block_jobs() | ||
40 | - | ||
41 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
42 | sync='full', node_name="repair0", replaces="img1", | ||
43 | target=quorum_repair_img, format=iotests.imgfmt) | ||
44 | @@ -XXX,XX +XXX,XX @@ class TestRepairQuorum(iotests.QMPTestCase): | ||
45 | 'target image does not match source after mirroring') | ||
46 | |||
47 | def test_pause(self): | ||
48 | - self.assert_no_active_block_jobs() | ||
49 | - | ||
50 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | ||
51 | sync='full', node_name="repair0", replaces="img1", | ||
52 | target=quorum_repair_img, format=iotests.imgfmt) | ||
53 | -- | ||
54 | 2.25.3 | ||
55 | |||
56 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Instead of looping in each test, let's better refactor vmdk target case | ||
4 | as a subclass. | ||
5 | |||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
7 | Message-Id: <20200430124713.3067-6-vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | tests/qemu-iotests/055 | 70 ++++++++++++++++++++------------------ | ||
11 | tests/qemu-iotests/055.out | 4 +-- | ||
12 | 2 files changed, 39 insertions(+), 35 deletions(-) | ||
13 | |||
14 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 | ||
15 | index XXXXXXX..XXXXXXX 100755 | ||
16 | --- a/tests/qemu-iotests/055 | ||
17 | +++ b/tests/qemu-iotests/055 | ||
18 | @@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase): | ||
19 | self.assert_no_active_block_jobs() | ||
20 | |||
21 | |||
22 | -class TestDriveCompression(iotests.QMPTestCase): | ||
23 | +class TestCompressedToQcow2(iotests.QMPTestCase): | ||
24 | image_len = 64 * 1024 * 1024 # MB | ||
25 | - fmt_supports_compression = [{'type': 'qcow2', 'args': ()}, | ||
26 | - {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}] | ||
27 | + target_fmt = {'type': 'qcow2', 'args': ()} | ||
28 | |||
29 | def tearDown(self): | ||
30 | self.vm.shutdown() | ||
31 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
32 | except OSError: | ||
33 | pass | ||
34 | |||
35 | - def do_prepare_drives(self, fmt, args, attach_target): | ||
36 | + def do_prepare_drives(self, attach_target): | ||
37 | self.vm = iotests.VM().add_drive('blkdebug::' + test_img) | ||
38 | |||
39 | - qemu_img('create', '-f', fmt, blockdev_target_img, | ||
40 | - str(TestDriveCompression.image_len), *args) | ||
41 | + qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img, | ||
42 | + str(self.image_len), *self.target_fmt['args']) | ||
43 | if attach_target: | ||
44 | self.vm.add_drive(blockdev_target_img, | ||
45 | - img_format=fmt, interface="none") | ||
46 | + img_format=self.target_fmt['type'], | ||
47 | + interface="none") | ||
48 | |||
49 | self.vm.launch() | ||
50 | |||
51 | - def do_test_compress_complete(self, cmd, format, attach_target, **args): | ||
52 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
53 | + def do_test_compress_complete(self, cmd, attach_target, **args): | ||
54 | + self.do_prepare_drives(attach_target) | ||
55 | |||
56 | self.assert_no_active_block_jobs() | ||
57 | |||
58 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
59 | |||
60 | self.vm.shutdown() | ||
61 | self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, | ||
62 | - iotests.imgfmt, format['type']), | ||
63 | + iotests.imgfmt, | ||
64 | + self.target_fmt['type']), | ||
65 | 'target image does not match source after backup') | ||
66 | |||
67 | def test_complete_compress_drive_backup(self): | ||
68 | - for format in TestDriveCompression.fmt_supports_compression: | ||
69 | - self.do_test_compress_complete('drive-backup', format, False, | ||
70 | - target=blockdev_target_img, mode='existing') | ||
71 | + self.do_test_compress_complete('drive-backup', False, | ||
72 | + target=blockdev_target_img, | ||
73 | + mode='existing') | ||
74 | |||
75 | def test_complete_compress_blockdev_backup(self): | ||
76 | - for format in TestDriveCompression.fmt_supports_compression: | ||
77 | - self.do_test_compress_complete('blockdev-backup', format, True, | ||
78 | - target='drive1') | ||
79 | + self.do_test_compress_complete('blockdev-backup', | ||
80 | + True, target='drive1') | ||
81 | |||
82 | - def do_test_compress_cancel(self, cmd, format, attach_target, **args): | ||
83 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
84 | + def do_test_compress_cancel(self, cmd, attach_target, **args): | ||
85 | + self.do_prepare_drives(attach_target) | ||
86 | |||
87 | self.assert_no_active_block_jobs() | ||
88 | |||
89 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
90 | self.vm.shutdown() | ||
91 | |||
92 | def test_compress_cancel_drive_backup(self): | ||
93 | - for format in TestDriveCompression.fmt_supports_compression: | ||
94 | - self.do_test_compress_cancel('drive-backup', format, False, | ||
95 | - target=blockdev_target_img, mode='existing') | ||
96 | + self.do_test_compress_cancel('drive-backup', False, | ||
97 | + target=blockdev_target_img, | ||
98 | + mode='existing') | ||
99 | |||
100 | def test_compress_cancel_blockdev_backup(self): | ||
101 | - for format in TestDriveCompression.fmt_supports_compression: | ||
102 | - self.do_test_compress_cancel('blockdev-backup', format, True, | ||
103 | - target='drive1') | ||
104 | + self.do_test_compress_cancel('blockdev-backup', True, | ||
105 | + target='drive1') | ||
106 | |||
107 | - def do_test_compress_pause(self, cmd, format, attach_target, **args): | ||
108 | - self.do_prepare_drives(format['type'], format['args'], attach_target) | ||
109 | + def do_test_compress_pause(self, cmd, attach_target, **args): | ||
110 | + self.do_prepare_drives(attach_target) | ||
111 | |||
112 | self.assert_no_active_block_jobs() | ||
113 | |||
114 | @@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase): | ||
115 | |||
116 | self.vm.shutdown() | ||
117 | self.assertTrue(iotests.compare_images(test_img, blockdev_target_img, | ||
118 | - iotests.imgfmt, format['type']), | ||
119 | + iotests.imgfmt, | ||
120 | + self.target_fmt['type']), | ||
121 | 'target image does not match source after backup') | ||
122 | |||
123 | def test_compress_pause_drive_backup(self): | ||
124 | - for format in TestDriveCompression.fmt_supports_compression: | ||
125 | - self.do_test_compress_pause('drive-backup', format, False, | ||
126 | - target=blockdev_target_img, mode='existing') | ||
127 | + self.do_test_compress_pause('drive-backup', False, | ||
128 | + target=blockdev_target_img, | ||
129 | + mode='existing') | ||
130 | |||
131 | def test_compress_pause_blockdev_backup(self): | ||
132 | - for format in TestDriveCompression.fmt_supports_compression: | ||
133 | - self.do_test_compress_pause('blockdev-backup', format, True, | ||
134 | - target='drive1') | ||
135 | + self.do_test_compress_pause('blockdev-backup', True, | ||
136 | + target='drive1') | ||
137 | + | ||
138 | + | ||
139 | +class TestCompressedToVmdk(TestCompressedToQcow2): | ||
140 | + target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} | ||
141 | + | ||
142 | |||
143 | if __name__ == '__main__': | ||
144 | iotests.main(supported_fmts=['raw', 'qcow2'], | ||
145 | diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out | ||
146 | index XXXXXXX..XXXXXXX 100644 | ||
147 | --- a/tests/qemu-iotests/055.out | ||
148 | +++ b/tests/qemu-iotests/055.out | ||
149 | @@ -XXX,XX +XXX,XX @@ | ||
150 | -.............................. | ||
151 | +.................................... | ||
152 | ---------------------------------------------------------------------- | ||
153 | -Ran 30 tests | ||
154 | +Ran 36 tests | ||
155 | |||
156 | OK | ||
157 | -- | ||
158 | 2.25.3 | ||
159 | |||
160 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-7-vsementsov@virtuozzo.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | --- | ||
7 | tests/qemu-iotests/055 | 4 ++++ | ||
8 | 1 file changed, 4 insertions(+) | ||
9 | |||
10 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 | ||
11 | index XXXXXXX..XXXXXXX 100755 | ||
12 | --- a/tests/qemu-iotests/055 | ||
13 | +++ b/tests/qemu-iotests/055 | ||
14 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): | ||
15 | class TestCompressedToVmdk(TestCompressedToQcow2): | ||
16 | target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} | ||
17 | |||
18 | + @iotests.skip_if_unsupported(['vmdk']) | ||
19 | + def setUp(self): | ||
20 | + pass | ||
21 | + | ||
22 | |||
23 | if __name__ == '__main__': | ||
24 | iotests.main(supported_fmts=['raw', 'qcow2'], | ||
25 | -- | ||
26 | 2.25.3 | ||
27 | |||
28 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-8-vsementsov@virtuozzo.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | --- | ||
7 | tests/qemu-iotests/109 | 1 + | ||
8 | 1 file changed, 1 insertion(+) | ||
9 | |||
10 | diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 | ||
11 | index XXXXXXX..XXXXXXX 100755 | ||
12 | --- a/tests/qemu-iotests/109 | ||
13 | +++ b/tests/qemu-iotests/109 | ||
14 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
15 | _supported_fmt raw | ||
16 | _supported_proto file | ||
17 | _supported_os Linux | ||
18 | +_require_drivers qcow qcow2 qed vdi vmdk vpc | ||
19 | |||
20 | qemu_comm_method=qmp | ||
21 | |||
22 | -- | ||
23 | 2.25.3 | ||
24 | |||
25 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
1 | 2 | ||
3 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Message-Id: <20200430124713.3067-9-vsementsov@virtuozzo.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | --- | ||
7 | tests/qemu-iotests/113 | 4 ++-- | ||
8 | 1 file changed, 2 insertions(+), 2 deletions(-) | ||
9 | |||
10 | diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 | ||
11 | index XXXXXXX..XXXXXXX 100755 | ||
12 | --- a/tests/qemu-iotests/113 | ||
13 | +++ b/tests/qemu-iotests/113 | ||
14 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
15 | . ./common.rc | ||
16 | . ./common.filter | ||
17 | |||
18 | -# Some of these test cases use bochs, but others do use raw, so this | ||
19 | -# is only half a lie. | ||
20 | +# Some of these test cases use bochs, but others do use raw | ||
21 | +_require_drivers bochs | ||
22 | _supported_fmt raw | ||
23 | _supported_proto file | ||
24 | _supported_os Linux | ||
25 | -- | ||
26 | 2.25.3 | ||
27 | |||
28 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | If you have a capable file system (tmpfs is good, ext4 not so much; | 3 | After commit f01643fb8b47e8a70c04bbf45e0f12a9e5bc54de when an image is |
4 | run ./check with TEST_DIR pointing to a good location so as not | 4 | extended and BDRV_REQ_ZERO_WRITE is set then the new clusters are |
5 | to skip the test), it's actually possible to create a qcow2 file | 5 | zeroized. |
6 | that expands to a sparse 512T image with just over 38M of content. | ||
7 | The test is not the world's fastest (qemu crawling through 256M | ||
8 | bits of refcount table to find the next cluster to allocate takes | ||
9 | several seconds, as does qemu-img check reporting millions of | ||
10 | leaked clusters); but it DOES catch the problem that the previous | ||
11 | patch just fixed where writing a compressed cluster to a full | ||
12 | image ended up overwriting the wrong cluster. | ||
13 | 6 | ||
14 | Suggested-by: Max Reitz <mreitz@redhat.com> | 7 | The code however does not detect correctly situations when the old and |
15 | Signed-off-by: Eric Blake <eblake@redhat.com> | 8 | the new end of the image are within the same cluster. The problem can |
16 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 9 | be reproduced with these steps: |
10 | |||
11 | qemu-img create -f qcow2 backing.qcow2 1M | ||
12 | qemu-img create -f qcow2 -F qcow2 -b backing.qcow2 top.qcow2 | ||
13 | qemu-img resize --shrink top.qcow2 520k | ||
14 | qemu-img resize top.qcow2 567k | ||
15 | |||
16 | In the last step offset - zero_start causes an integer wraparound. | ||
17 | |||
18 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
19 | Message-Id: <20200504155217.10325-1-berto@igalia.com> | ||
20 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | --- | 22 | --- |
19 | tests/qemu-iotests/220 | 96 ++++++++++++++++++++++++++++++++++++++ | 23 | block/qcow2.c | 12 ++++--- |
20 | tests/qemu-iotests/220.out | 54 +++++++++++++++++++++ | 24 | tests/qemu-iotests/292 | 73 ++++++++++++++++++++++++++++++++++++++ |
25 | tests/qemu-iotests/292.out | 24 +++++++++++++ | ||
21 | tests/qemu-iotests/group | 1 + | 26 | tests/qemu-iotests/group | 1 + |
22 | 3 files changed, 151 insertions(+) | 27 | 4 files changed, 105 insertions(+), 5 deletions(-) |
23 | create mode 100755 tests/qemu-iotests/220 | 28 | create mode 100755 tests/qemu-iotests/292 |
24 | create mode 100644 tests/qemu-iotests/220.out | 29 | create mode 100644 tests/qemu-iotests/292.out |
25 | 30 | ||
26 | diff --git a/tests/qemu-iotests/220 b/tests/qemu-iotests/220 | 31 | diff --git a/block/qcow2.c b/block/qcow2.c |
32 | index XXXXXXX..XXXXXXX 100644 | ||
33 | --- a/block/qcow2.c | ||
34 | +++ b/block/qcow2.c | ||
35 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, | ||
36 | * requires a cluster-aligned start. The end may be unaligned if it is | ||
37 | * at the end of the image (which it is here). | ||
38 | */ | ||
39 | - ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0); | ||
40 | - if (ret < 0) { | ||
41 | - error_setg_errno(errp, -ret, "Failed to zero out new clusters"); | ||
42 | - goto fail; | ||
43 | + if (offset > zero_start) { | ||
44 | + ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0); | ||
45 | + if (ret < 0) { | ||
46 | + error_setg_errno(errp, -ret, "Failed to zero out new clusters"); | ||
47 | + goto fail; | ||
48 | + } | ||
49 | } | ||
50 | |||
51 | /* Write explicit zeros for the unaligned head */ | ||
52 | if (zero_start > old_length) { | ||
53 | - uint64_t len = zero_start - old_length; | ||
54 | + uint64_t len = MIN(zero_start, offset) - old_length; | ||
55 | uint8_t *buf = qemu_blockalign0(bs, len); | ||
56 | QEMUIOVector qiov; | ||
57 | qemu_iovec_init_buf(&qiov, buf, len); | ||
58 | diff --git a/tests/qemu-iotests/292 b/tests/qemu-iotests/292 | ||
27 | new file mode 100755 | 59 | new file mode 100755 |
28 | index XXXXXXX..XXXXXXX | 60 | index XXXXXXX..XXXXXXX |
29 | --- /dev/null | 61 | --- /dev/null |
30 | +++ b/tests/qemu-iotests/220 | 62 | +++ b/tests/qemu-iotests/292 |
31 | @@ -XXX,XX +XXX,XX @@ | 63 | @@ -XXX,XX +XXX,XX @@ |
32 | +#!/bin/bash | 64 | +#!/usr/bin/env bash |
33 | +# | 65 | +# |
34 | +# max limits on compression in huge qcow2 files | 66 | +# Test resizing a qcow2 image with a backing file |
35 | +# | 67 | +# |
36 | +# Copyright (C) 2018 Red Hat, Inc. | 68 | +# Copyright (C) 2020 Igalia, S.L. |
69 | +# Author: Alberto Garcia <berto@igalia.com> | ||
37 | +# | 70 | +# |
38 | +# This program is free software; you can redistribute it and/or modify | 71 | +# This program is free software; you can redistribute it and/or modify |
39 | +# it under the terms of the GNU General Public License as published by | 72 | +# it under the terms of the GNU General Public License as published by |
40 | +# the Free Software Foundation; either version 2 of the License, or | 73 | +# the Free Software Foundation; either version 2 of the License, or |
41 | +# (at your option) any later version. | 74 | +# (at your option) any later version. |
... | ... | ||
47 | +# | 80 | +# |
48 | +# You should have received a copy of the GNU General Public License | 81 | +# You should have received a copy of the GNU General Public License |
49 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | 82 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
50 | +# | 83 | +# |
51 | + | 84 | + |
52 | +seq=$(basename $0) | 85 | +# creator |
86 | +owner=berto@igalia.com | ||
87 | + | ||
88 | +seq=`basename $0` | ||
53 | +echo "QA output created by $seq" | 89 | +echo "QA output created by $seq" |
54 | + | 90 | + |
55 | +status=1 # failure is the default! | 91 | +status=1 # failure is the default! |
56 | + | 92 | + |
57 | +_cleanup() | 93 | +_cleanup() |
... | ... | ||
61 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | 97 | +trap "_cleanup; exit \$status" 0 1 2 3 15 |
62 | + | 98 | + |
63 | +# get standard environment, filters and checks | 99 | +# get standard environment, filters and checks |
64 | +. ./common.rc | 100 | +. ./common.rc |
65 | +. ./common.filter | 101 | +. ./common.filter |
66 | +. ./common.pattern | ||
67 | + | 102 | + |
68 | +_supported_fmt qcow2 | 103 | +_supported_fmt qcow2 |
69 | +_supported_proto file | 104 | +_supported_proto file |
70 | +_supported_os Linux | 105 | +_supported_os Linux |
71 | + | 106 | + |
72 | +echo "== Creating huge file ==" | 107 | +echo '### Create the backing image' |
108 | +BACKING_IMG="$TEST_IMG.base" | ||
109 | +TEST_IMG="$BACKING_IMG" _make_test_img 1M | ||
73 | + | 110 | + |
74 | +# Sanity check: We require a file system that permits the creation | 111 | +echo '### Fill the backing image with data (0x11)' |
75 | +# of a HUGE (but very sparse) file. tmpfs works, ext4 does not. | 112 | +$QEMU_IO -c 'write -P 0x11 0 1M' "$BACKING_IMG" | _filter_qemu_io |
76 | +if ! truncate --size=513T "$TEST_IMG"; then | ||
77 | + _notrun "file system on $TEST_DIR does not support large enough files" | ||
78 | +fi | ||
79 | +rm "$TEST_IMG" | ||
80 | +IMGOPTS='cluster_size=2M,refcount_bits=1' _make_test_img 513T | ||
81 | + | 113 | + |
82 | +echo "== Populating refcounts ==" | 114 | +echo '### Create the top image' |
83 | +# We want an image with 256M refcounts * 2M clusters = 512T referenced. | 115 | +_make_test_img -F "$IMGFMT" -b "$BACKING_IMG" |
84 | +# Each 2M cluster holds 16M refcounts; the refcount table initially uses | ||
85 | +# 1 refblock, so we need to add 15 more. The refcount table lives at 2M, | ||
86 | +# first refblock at 4M, L2 at 6M, so our remaining additions start at 8M. | ||
87 | +# Then, for each refblock, mark it as fully populated. | ||
88 | +to_hex() { | ||
89 | + printf %016x\\n $1 | sed 's/\(..\)/\\x\1/g' | ||
90 | +} | ||
91 | +truncate --size=38m "$TEST_IMG" | ||
92 | +entry=$((0x200000)) | ||
93 | +$QEMU_IO_PROG -f raw -c "w -P 0xff 4m 2m" "$TEST_IMG" | _filter_qemu_io | ||
94 | +for i in {1..15}; do | ||
95 | + offs=$((0x600000 + i*0x200000)) | ||
96 | + poke_file "$TEST_IMG" $((i*8 + entry)) $(to_hex $offs) | ||
97 | + $QEMU_IO_PROG -f raw -c "w -P 0xff $offs 2m" "$TEST_IMG" | _filter_qemu_io | ||
98 | +done | ||
99 | + | 116 | + |
100 | +echo "== Checking file before ==" | 117 | +echo '### Fill the top image with data (0x22)' |
101 | +# FIXME: 'qemu-img check' doesn't diagnose refcounts beyond the end of | 118 | +$QEMU_IO -c 'write -P 0x22 0 1M' "$TEST_IMG" | _filter_qemu_io |
102 | +# the file as leaked clusters | ||
103 | +_check_test_img 2>&1 | sed '/^Leaked cluster/d' | ||
104 | +stat -c 'image size %s' "$TEST_IMG" | ||
105 | + | 119 | + |
106 | +echo "== Trying to write compressed cluster ==" | 120 | +# Both offsets are part of the same cluster. |
107 | +# Given our file size, the next available cluster at 512T lies beyond the | 121 | +echo '### Shrink the image to 520k' |
108 | +# maximum offset that a compressed 2M cluster can reside in | 122 | +$QEMU_IMG resize --shrink "$TEST_IMG" 520k |
109 | +$QEMU_IO_PROG -c 'w -c 0 2m' "$TEST_IMG" | _filter_qemu_io | 123 | +echo '### Grow the image to 567k' |
110 | +# The attempt failed, but ended up allocating a new refblock | 124 | +$QEMU_IMG resize "$TEST_IMG" 567k |
111 | +stat -c 'image size %s' "$TEST_IMG" | ||
112 | + | 125 | + |
113 | +echo "== Writing normal cluster ==" | 126 | +echo '### Check that the tail of the image reads as zeroes' |
114 | +# The failed write should not corrupt the image, so a normal write succeeds | 127 | +$QEMU_IO -c 'read -P 0x22 0 520k' "$TEST_IMG" | _filter_qemu_io |
115 | +$QEMU_IO_PROG -c 'w 0 2m' "$TEST_IMG" | _filter_qemu_io | 128 | +$QEMU_IO -c 'read -P 0x00 520k 47k' "$TEST_IMG" | _filter_qemu_io |
116 | + | 129 | + |
117 | +echo "== Checking file after ==" | 130 | +echo '### Show output of qemu-img map' |
118 | +# qemu-img now sees the millions of leaked clusters, thanks to the allocations | 131 | +$QEMU_IMG map "$TEST_IMG" | _filter_testdir |
119 | +# at 512T. Undo many of our faked references to speed up the check. | ||
120 | +$QEMU_IO_PROG -f raw -c "w -z 5m 1m" -c "w -z 8m 30m" "$TEST_IMG" | | ||
121 | + _filter_qemu_io | ||
122 | +_check_test_img 2>&1 | sed '/^Leaked cluster/d' | ||
123 | + | 132 | + |
124 | +# success, all done | 133 | +# success, all done |
125 | +echo "*** done" | 134 | +echo "*** done" |
126 | +rm -f $seq.full | 135 | +rm -f $seq.full |
127 | +status=0 | 136 | +status=0 |
128 | diff --git a/tests/qemu-iotests/220.out b/tests/qemu-iotests/220.out | 137 | diff --git a/tests/qemu-iotests/292.out b/tests/qemu-iotests/292.out |
129 | new file mode 100644 | 138 | new file mode 100644 |
130 | index XXXXXXX..XXXXXXX | 139 | index XXXXXXX..XXXXXXX |
131 | --- /dev/null | 140 | --- /dev/null |
132 | +++ b/tests/qemu-iotests/220.out | 141 | +++ b/tests/qemu-iotests/292.out |
133 | @@ -XXX,XX +XXX,XX @@ | 142 | @@ -XXX,XX +XXX,XX @@ |
134 | +QA output created by 220 | 143 | +QA output created by 292 |
135 | +== Creating huge file == | 144 | +### Create the backing image |
136 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=564049465049088 | 145 | +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 |
137 | +== Populating refcounts == | 146 | +### Fill the backing image with data (0x11) |
138 | +wrote 2097152/2097152 bytes at offset 4194304 | 147 | +wrote 1048576/1048576 bytes at offset 0 |
139 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
140 | +wrote 2097152/2097152 bytes at offset 8388608 | ||
141 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
142 | +wrote 2097152/2097152 bytes at offset 10485760 | ||
143 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
144 | +wrote 2097152/2097152 bytes at offset 12582912 | ||
145 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
146 | +wrote 2097152/2097152 bytes at offset 14680064 | ||
147 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
148 | +wrote 2097152/2097152 bytes at offset 16777216 | ||
149 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
150 | +wrote 2097152/2097152 bytes at offset 18874368 | ||
151 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
152 | +wrote 2097152/2097152 bytes at offset 20971520 | ||
153 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
154 | +wrote 2097152/2097152 bytes at offset 23068672 | ||
155 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
156 | +wrote 2097152/2097152 bytes at offset 25165824 | ||
157 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
158 | +wrote 2097152/2097152 bytes at offset 27262976 | ||
159 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
160 | +wrote 2097152/2097152 bytes at offset 29360128 | ||
161 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
162 | +wrote 2097152/2097152 bytes at offset 31457280 | ||
163 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
164 | +wrote 2097152/2097152 bytes at offset 33554432 | ||
165 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
166 | +wrote 2097152/2097152 bytes at offset 35651584 | ||
167 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
168 | +wrote 2097152/2097152 bytes at offset 37748736 | ||
169 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
170 | +== Checking file before == | ||
171 | +No errors were found on the image. | ||
172 | +image size 39845888 | ||
173 | +== Trying to write compressed cluster == | ||
174 | +write failed: Input/output error | ||
175 | +image size 562949957615616 | ||
176 | +== Writing normal cluster == | ||
177 | +wrote 2097152/2097152 bytes at offset 0 | ||
178 | +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
179 | +== Checking file after == | ||
180 | +wrote 1048576/1048576 bytes at offset 5242880 | ||
181 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 148 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
182 | +wrote 31457280/31457280 bytes at offset 8388608 | 149 | +### Create the top image |
183 | +30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 150 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT |
184 | + | 151 | +### Fill the top image with data (0x22) |
185 | +8388589 leaked clusters were found on the image. | 152 | +wrote 1048576/1048576 bytes at offset 0 |
186 | +This means waste of disk space, but no harm to data. | 153 | +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
154 | +### Shrink the image to 520k | ||
155 | +Image resized. | ||
156 | +### Grow the image to 567k | ||
157 | +Image resized. | ||
158 | +### Check that the tail of the image reads as zeroes | ||
159 | +read 532480/532480 bytes at offset 0 | ||
160 | +520 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
161 | +read 48128/48128 bytes at offset 532480 | ||
162 | +47 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
163 | +### Show output of qemu-img map | ||
164 | +Offset Length Mapped to File | ||
165 | +0 0x8dc00 0x50000 TEST_DIR/t.qcow2 | ||
187 | +*** done | 166 | +*** done |
188 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | 167 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group |
189 | index XXXXXXX..XXXXXXX 100644 | 168 | index XXXXXXX..XXXXXXX 100644 |
190 | --- a/tests/qemu-iotests/group | 169 | --- a/tests/qemu-iotests/group |
191 | +++ b/tests/qemu-iotests/group | 170 | +++ b/tests/qemu-iotests/group |
192 | @@ -XXX,XX +XXX,XX @@ | 171 | @@ -XXX,XX +XXX,XX @@ |
193 | 217 rw auto quick | 172 | 288 quick |
194 | 218 rw auto quick | 173 | 289 rw quick |
195 | 219 rw auto | 174 | 290 rw auto quick |
196 | +220 rw auto | 175 | +292 rw auto quick |
197 | 221 rw auto quick | ||
198 | 222 rw auto quick | ||
199 | 223 rw auto quick | ||
200 | -- | 176 | -- |
201 | 2.19.1 | 177 | 2.25.3 |
202 | 178 | ||
203 | 179 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | m_data is used for zero clusters even though valid == 0. It really only | ||
2 | means that a new cluster was allocated in the image file. Rename it to | ||
3 | reflect this. | ||
1 | 4 | ||
5 | While at it, change it from int to bool, too. | ||
6 | |||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20200430133007.170335-2-kwolf@redhat.com> | ||
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | block/vmdk.c | 8 ++++---- | ||
13 | 1 file changed, 4 insertions(+), 4 deletions(-) | ||
14 | |||
15 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block/vmdk.c | ||
18 | +++ b/block/vmdk.c | ||
19 | @@ -XXX,XX +XXX,XX @@ typedef struct VmdkMetaData { | ||
20 | unsigned int l1_index; | ||
21 | unsigned int l2_index; | ||
22 | unsigned int l2_offset; | ||
23 | - int valid; | ||
24 | + bool new_allocation; | ||
25 | uint32_t *l2_cache_entry; | ||
26 | } VmdkMetaData; | ||
27 | |||
28 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
29 | unsigned int l2_size_bytes = extent->l2_size * extent->entry_size; | ||
30 | |||
31 | if (m_data) { | ||
32 | - m_data->valid = 0; | ||
33 | + m_data->new_allocation = false; | ||
34 | } | ||
35 | if (extent->flat) { | ||
36 | *cluster_offset = extent->flat_start_offset; | ||
37 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
38 | return ret; | ||
39 | } | ||
40 | if (m_data) { | ||
41 | - m_data->valid = 1; | ||
42 | + m_data->new_allocation = true; | ||
43 | m_data->l1_index = l1_index; | ||
44 | m_data->l2_index = l2_index; | ||
45 | m_data->l2_offset = l2_offset; | ||
46 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, | ||
47 | if (ret) { | ||
48 | return ret; | ||
49 | } | ||
50 | - if (m_data.valid) { | ||
51 | + if (m_data.new_allocation) { | ||
52 | /* update L2 tables */ | ||
53 | if (vmdk_L2update(extent, &m_data, | ||
54 | cluster_offset >> BDRV_SECTOR_BITS) | ||
55 | -- | ||
56 | 2.25.3 | ||
57 | |||
58 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | m_data must contain valid data even for zero clusters when no cluster | ||
2 | was allocated in the image file. Without this, zero writes segfault with | ||
3 | images that have zeroed_grain=on. | ||
1 | 4 | ||
5 | For zero writes, we don't want to allocate a cluster in the image file | ||
6 | even in compressed files. | ||
7 | |||
8 | Fixes: 524089bce43fd1cd3daaca979872451efa2cf7c6 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Message-Id: <20200430133007.170335-3-kwolf@redhat.com> | ||
11 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | block/vmdk.c | 12 +++++++----- | ||
15 | 1 file changed, 7 insertions(+), 5 deletions(-) | ||
16 | |||
17 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/vmdk.c | ||
20 | +++ b/block/vmdk.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
22 | extent->l2_cache_counts[min_index] = 1; | ||
23 | found: | ||
24 | l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; | ||
25 | + if (m_data) { | ||
26 | + m_data->l1_index = l1_index; | ||
27 | + m_data->l2_index = l2_index; | ||
28 | + m_data->l2_offset = l2_offset; | ||
29 | + m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index; | ||
30 | + } | ||
31 | |||
32 | if (extent->sesparse) { | ||
33 | cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]); | ||
34 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
35 | } | ||
36 | if (m_data) { | ||
37 | m_data->new_allocation = true; | ||
38 | - m_data->l1_index = l1_index; | ||
39 | - m_data->l2_index = l2_index; | ||
40 | - m_data->l2_offset = l2_offset; | ||
41 | - m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index; | ||
42 | } | ||
43 | } | ||
44 | *cluster_offset = cluster_sector << BDRV_SECTOR_BITS; | ||
45 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, | ||
46 | error_report("Could not write to allocated cluster" | ||
47 | " for streamOptimized"); | ||
48 | return -EIO; | ||
49 | - } else { | ||
50 | + } else if (!zeroed) { | ||
51 | /* allocate */ | ||
52 | ret = get_cluster_offset(bs, extent, &m_data, offset, | ||
53 | true, &cluster_offset, 0, 0); | ||
54 | -- | ||
55 | 2.25.3 | ||
56 | |||
57 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | When overwriting a zero cluster, we must not perform copy-on-write from | ||
2 | the backing file, but from a zeroed buffer. | ||
1 | 3 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | Message-Id: <20200430133007.170335-4-kwolf@redhat.com> | ||
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | block/vmdk.c | 18 ++++++++++++------ | ||
10 | 1 file changed, 12 insertions(+), 6 deletions(-) | ||
11 | |||
12 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/block/vmdk.c | ||
15 | +++ b/block/vmdk.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp) | ||
17 | * get_whole_cluster | ||
18 | * | ||
19 | * Copy backing file's cluster that covers @sector_num, otherwise write zero, | ||
20 | - * to the cluster at @cluster_sector_num. | ||
21 | + * to the cluster at @cluster_sector_num. If @zeroed is true, we're overwriting | ||
22 | + * a zeroed cluster in the current layer and must not copy data from the | ||
23 | + * backing file. | ||
24 | * | ||
25 | * If @skip_start_sector < @skip_end_sector, the relative range | ||
26 | * [@skip_start_sector, @skip_end_sector) is not copied or written, and leave | ||
27 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, | ||
28 | uint64_t cluster_offset, | ||
29 | uint64_t offset, | ||
30 | uint64_t skip_start_bytes, | ||
31 | - uint64_t skip_end_bytes) | ||
32 | + uint64_t skip_end_bytes, | ||
33 | + bool zeroed) | ||
34 | { | ||
35 | int ret = VMDK_OK; | ||
36 | int64_t cluster_bytes; | ||
37 | uint8_t *whole_grain; | ||
38 | + bool copy_from_backing; | ||
39 | |||
40 | /* For COW, align request sector_num to cluster start */ | ||
41 | cluster_bytes = extent->cluster_sectors << BDRV_SECTOR_BITS; | ||
42 | offset = QEMU_ALIGN_DOWN(offset, cluster_bytes); | ||
43 | whole_grain = qemu_blockalign(bs, cluster_bytes); | ||
44 | + copy_from_backing = bs->backing && !zeroed; | ||
45 | |||
46 | - if (!bs->backing) { | ||
47 | + if (!copy_from_backing) { | ||
48 | memset(whole_grain, 0, skip_start_bytes); | ||
49 | memset(whole_grain + skip_end_bytes, 0, cluster_bytes - skip_end_bytes); | ||
50 | } | ||
51 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, | ||
52 | |||
53 | /* Read backing data before skip range */ | ||
54 | if (skip_start_bytes > 0) { | ||
55 | - if (bs->backing) { | ||
56 | + if (copy_from_backing) { | ||
57 | /* qcow2 emits this on bs->file instead of bs->backing */ | ||
58 | BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); | ||
59 | ret = bdrv_pread(bs->backing, offset, whole_grain, | ||
60 | @@ -XXX,XX +XXX,XX @@ static int get_whole_cluster(BlockDriverState *bs, | ||
61 | } | ||
62 | /* Read backing data after skip range */ | ||
63 | if (skip_end_bytes < cluster_bytes) { | ||
64 | - if (bs->backing) { | ||
65 | + if (copy_from_backing) { | ||
66 | /* qcow2 emits this on bs->file instead of bs->backing */ | ||
67 | BLKDBG_EVENT(extent->file, BLKDBG_COW_READ); | ||
68 | ret = bdrv_pread(bs->backing, offset + skip_end_bytes, | ||
69 | @@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs, | ||
70 | * or inappropriate VM shutdown. | ||
71 | */ | ||
72 | ret = get_whole_cluster(bs, extent, cluster_sector * BDRV_SECTOR_SIZE, | ||
73 | - offset, skip_start_bytes, skip_end_bytes); | ||
74 | + offset, skip_start_bytes, skip_end_bytes, | ||
75 | + zeroed); | ||
76 | if (ret) { | ||
77 | return ret; | ||
78 | } | ||
79 | -- | ||
80 | 2.25.3 | ||
81 | |||
82 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | If a cluster is already zeroed, we don't have to call vmdk_L2update(), | ||
2 | which is rather slow because it flushes the image file. | ||
1 | 3 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | Message-Id: <20200430133007.170335-5-kwolf@redhat.com> | ||
6 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | block/vmdk.c | 2 +- | ||
10 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
11 | |||
12 | diff --git a/block/vmdk.c b/block/vmdk.c | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/block/vmdk.c | ||
15 | +++ b/block/vmdk.c | ||
16 | @@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset, | ||
17 | offset_in_cluster == 0 && | ||
18 | n_bytes >= extent->cluster_sectors * BDRV_SECTOR_SIZE) { | ||
19 | n_bytes = extent->cluster_sectors * BDRV_SECTOR_SIZE; | ||
20 | - if (!zero_dry_run) { | ||
21 | + if (!zero_dry_run && ret != VMDK_ZEROED) { | ||
22 | /* update L2 tables */ | ||
23 | if (vmdk_L2update(extent, &m_data, VMDK_GTE_ZEROED) | ||
24 | != VMDK_OK) { | ||
25 | -- | ||
26 | 2.25.3 | ||
27 | |||
28 | diff view generated by jsdifflib |
1 | From: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> | 1 | If we have a backup L2 table, we currently flush once after writing to |
---|---|---|---|
2 | the active L2 table and again after writing to the backup table. A | ||
3 | single flush is enough and makes things a little less slow. | ||
2 | 4 | ||
3 | Commit c8a35f1cf0f "fdc: use IsaDma interface instead of global DMA_* | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | functions" accidentally introduced a segfault in fdctrl_stop_transfer() for | 6 | Message-Id: <20200430133007.170335-6-kwolf@redhat.com> |
5 | non-DMA transfers. | 7 | Reviewed-by: Eric Blake <eblake@redhat.com> |
6 | |||
7 | If fdctrl->dma_chann has not been configured then the fdctrl->dma interface | ||
8 | reference isn't initialised during isabus_fdc_realize(). Unfortunately | ||
9 | fdctrl_stop_transfer() unconditionally references the DMA interface when | ||
10 | finishing the transfer causing a NULL pointer dereference. | ||
11 | |||
12 | Fix the issue by adding a check in fdctrl_stop_transfer() so that the DMA | ||
13 | interface reference and release method is only invoked if fdctrl->dma_chann | ||
14 | has been set. | ||
15 | |||
16 | (This issue was discovered by Martin testing a recent change in the NetBSD | ||
17 | installer under qemu-system-sparc) | ||
18 | |||
19 | Cc: qemu-stable@nongnu.org | ||
20 | Reported-by: Martin Husemann <martin@duskware.de> | ||
21 | Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> | ||
22 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> | ||
23 | Reviewed-by: Hervé Poussineau <hpoussin@reactos.org> | ||
24 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
25 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
26 | --- | 9 | --- |
27 | hw/block/fdc.c | 2 +- | 10 | block/vmdk.c | 7 +++++-- |
28 | 1 file changed, 1 insertion(+), 1 deletion(-) | 11 | 1 file changed, 5 insertions(+), 2 deletions(-) |
29 | 12 | ||
30 | diff --git a/hw/block/fdc.c b/hw/block/fdc.c | 13 | diff --git a/block/vmdk.c b/block/vmdk.c |
31 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
32 | --- a/hw/block/fdc.c | 15 | --- a/block/vmdk.c |
33 | +++ b/hw/block/fdc.c | 16 | +++ b/block/vmdk.c |
34 | @@ -XXX,XX +XXX,XX @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, | 17 | @@ -XXX,XX +XXX,XX @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, |
35 | fdctrl->fifo[5] = cur_drv->sect; | 18 | offset = cpu_to_le32(offset); |
36 | fdctrl->fifo[6] = FD_SECTOR_SC; | 19 | /* update L2 table */ |
37 | fdctrl->data_dir = FD_DIR_READ; | 20 | BLKDBG_EVENT(extent->file, BLKDBG_L2_UPDATE); |
38 | - if (!(fdctrl->msr & FD_MSR_NONDMA)) { | 21 | - if (bdrv_pwrite_sync(extent->file, |
39 | + if (fdctrl->dma_chann != -1 && !(fdctrl->msr & FD_MSR_NONDMA)) { | 22 | + if (bdrv_pwrite(extent->file, |
40 | IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); | 23 | ((int64_t)m_data->l2_offset * 512) |
41 | k->release_DREQ(fdctrl->dma, fdctrl->dma_chann); | 24 | + (m_data->l2_index * sizeof(offset)), |
25 | &offset, sizeof(offset)) < 0) { | ||
26 | @@ -XXX,XX +XXX,XX @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, | ||
27 | /* update backup L2 table */ | ||
28 | if (extent->l1_backup_table_offset != 0) { | ||
29 | m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; | ||
30 | - if (bdrv_pwrite_sync(extent->file, | ||
31 | + if (bdrv_pwrite(extent->file, | ||
32 | ((int64_t)m_data->l2_offset * 512) | ||
33 | + (m_data->l2_index * sizeof(offset)), | ||
34 | &offset, sizeof(offset)) < 0) { | ||
35 | return VMDK_ERROR; | ||
36 | } | ||
37 | } | ||
38 | + if (bdrv_flush(extent->file->bs) < 0) { | ||
39 | + return VMDK_ERROR; | ||
40 | + } | ||
41 | if (m_data->l2_cache_entry) { | ||
42 | *m_data->l2_cache_entry = offset; | ||
42 | } | 43 | } |
43 | -- | 44 | -- |
44 | 2.19.1 | 45 | 2.25.3 |
45 | 46 | ||
46 | 47 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | In order to avoid bitrot in the zero cluster code in VMDK, enable | ||
2 | zeroed_grain=on by default for the tests. | ||
1 | 3 | ||
4 | 059 now unsets the default options because zeroed_grain=on works only | ||
5 | with some subformats and the test case tests many different subformats, | ||
6 | including those for which it doesn't work. | ||
7 | |||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Message-Id: <20200430133007.170335-7-kwolf@redhat.com> | ||
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | --- | ||
13 | tests/qemu-iotests/059 | 6 +++--- | ||
14 | tests/qemu-iotests/check | 3 +++ | ||
15 | 2 files changed, 6 insertions(+), 3 deletions(-) | ||
16 | |||
17 | diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 | ||
18 | index XXXXXXX..XXXXXXX 100755 | ||
19 | --- a/tests/qemu-iotests/059 | ||
20 | +++ b/tests/qemu-iotests/059 | ||
21 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
22 | _supported_fmt vmdk | ||
23 | _supported_proto file | ||
24 | _supported_os Linux | ||
25 | -_unsupported_imgopts "subformat=monolithicFlat" \ | ||
26 | - "subformat=twoGbMaxExtentFlat" \ | ||
27 | - "subformat=twoGbMaxExtentSparse" | ||
28 | + | ||
29 | +# We test all kinds of VMDK options here, so ignore user-specified options | ||
30 | +IMGOPTS="" | ||
31 | |||
32 | capacity_offset=16 | ||
33 | granularity_offset=20 | ||
34 | diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check | ||
35 | index XXXXXXX..XXXXXXX 100755 | ||
36 | --- a/tests/qemu-iotests/check | ||
37 | +++ b/tests/qemu-iotests/check | ||
38 | @@ -XXX,XX +XXX,XX @@ fi | ||
39 | if [ "$IMGFMT" == "luks" ] && ! (echo "$IMGOPTS" | grep "iter-time=" > /dev/null); then | ||
40 | IMGOPTS=$(_optstr_add "$IMGOPTS" "iter-time=10") | ||
41 | fi | ||
42 | +if [ "$IMGFMT" == "vmdk" ] && ! (echo "$IMGOPTS" | grep "zeroed_grain=" > /dev/null); then | ||
43 | + IMGOPTS=$(_optstr_add "$IMGOPTS" "zeroed_grain=on") | ||
44 | +fi | ||
45 | |||
46 | if [ -z "$SAMPLE_IMG_DIR" ]; then | ||
47 | SAMPLE_IMG_DIR="$source_iotests/sample_images" | ||
48 | -- | ||
49 | 2.25.3 | ||
50 | |||
51 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | The test case forgot to specify the null-co size for the target node. | ||
2 | When adding a check to backup that both sizes match, this would fail | ||
3 | because of the size mismatch and not the behaviour that the test really | ||
4 | wanted to test. | ||
1 | 5 | ||
6 | Fixes: a541fcc27c98b96da187c7d4573f3270f3ddd283 | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | Message-Id: <20200430142755.315494-2-kwolf@redhat.com> | ||
9 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | tests/qemu-iotests/283 | 6 +++++- | ||
13 | tests/qemu-iotests/283.out | 2 +- | ||
14 | 2 files changed, 6 insertions(+), 2 deletions(-) | ||
15 | |||
16 | diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283 | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/tests/qemu-iotests/283 | ||
19 | +++ b/tests/qemu-iotests/283 | ||
20 | @@ -XXX,XX +XXX,XX @@ to check that crash is fixed :) | ||
21 | vm = iotests.VM() | ||
22 | vm.launch() | ||
23 | |||
24 | -vm.qmp_log('blockdev-add', **{'node-name': 'target', 'driver': 'null-co'}) | ||
25 | +vm.qmp_log('blockdev-add', **{ | ||
26 | + 'node-name': 'target', | ||
27 | + 'driver': 'null-co', | ||
28 | + 'size': size, | ||
29 | +}) | ||
30 | |||
31 | vm.qmp_log('blockdev-add', **{ | ||
32 | 'node-name': 'source', | ||
33 | diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out | ||
34 | index XXXXXXX..XXXXXXX 100644 | ||
35 | --- a/tests/qemu-iotests/283.out | ||
36 | +++ b/tests/qemu-iotests/283.out | ||
37 | @@ -XXX,XX +XXX,XX @@ | ||
38 | -{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}} | ||
39 | +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target", "size": 1048576}} | ||
40 | {"return": {}} | ||
41 | {"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": {"driver": "null-co", "node-name": "base", "size": 1048576}, "node-name": "source"}} | ||
42 | {"return": {}} | ||
43 | -- | ||
44 | 2.25.3 | ||
45 | |||
46 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | bdrv_get_device_name() will be an empty string with modern management | ||
2 | tools that don't use -drive. Use bdrv_get_device_or_node_name() instead | ||
3 | so that the node name is used if the BlockBackend is anonymous. | ||
1 | 4 | ||
5 | While at it, start with upper case to make the message consistent with | ||
6 | the rest of the function. | ||
7 | |||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
10 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
11 | Message-Id: <20200430142755.315494-3-kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | block/backup.c | 4 ++-- | ||
15 | 1 file changed, 2 insertions(+), 2 deletions(-) | ||
16 | |||
17 | diff --git a/block/backup.c b/block/backup.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/backup.c | ||
20 | +++ b/block/backup.c | ||
21 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
22 | |||
23 | len = bdrv_getlength(bs); | ||
24 | if (len < 0) { | ||
25 | - error_setg_errno(errp, -len, "unable to get length for '%s'", | ||
26 | - bdrv_get_device_name(bs)); | ||
27 | + error_setg_errno(errp, -len, "Unable to get length for '%s'", | ||
28 | + bdrv_get_device_or_node_name(bs)); | ||
29 | goto error; | ||
30 | } | ||
31 | |||
32 | -- | ||
33 | 2.25.3 | ||
34 | |||
35 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | Since the introduction of a backup filter node in commit 00e30f05d, the | ||
2 | backup block job crashes when the target image is smaller than the | ||
3 | source image because it will try to write after the end of the target | ||
4 | node without having BLK_PERM_RESIZE. (Previously, the BlockBackend layer | ||
5 | would have caught this and errored out gracefully.) | ||
1 | 6 | ||
7 | We can fix this and even do better than the old behaviour: Check that | ||
8 | source and target have the same image size at the start of the block job | ||
9 | and unshare BLK_PERM_RESIZE. (This permission was already unshared | ||
10 | before the same commit 00e30f05d, but the BlockBackend that was used to | ||
11 | make the restriction was removed without a replacement.) This will | ||
12 | immediately error out when starting the job instead of only when writing | ||
13 | to a block that doesn't exist in the target. | ||
14 | |||
15 | Longer target than source would technically work because we would never | ||
16 | write to blocks that don't exist, but semantically these are invalid, | ||
17 | too, because a backup is supposed to create a copy, not just an image | ||
18 | that starts with a copy. | ||
19 | |||
20 | Fixes: 00e30f05de1d19586345ec373970ef4c192c6270 | ||
21 | Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1778593 | ||
22 | Cc: qemu-stable@nongnu.org | ||
23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
24 | Message-Id: <20200430142755.315494-4-kwolf@redhat.com> | ||
25 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
27 | --- | ||
28 | block/backup-top.c | 14 +++++++++----- | ||
29 | block/backup.c | 14 +++++++++++++- | ||
30 | 2 files changed, 22 insertions(+), 6 deletions(-) | ||
31 | |||
32 | diff --git a/block/backup-top.c b/block/backup-top.c | ||
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/block/backup-top.c | ||
35 | +++ b/block/backup-top.c | ||
36 | @@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
37 | * | ||
38 | * Share write to target (child_file), to not interfere | ||
39 | * with guest writes to its disk which may be in target backing chain. | ||
40 | + * Can't resize during a backup block job because we check the size | ||
41 | + * only upfront. | ||
42 | */ | ||
43 | - *nshared = BLK_PERM_ALL; | ||
44 | + *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; | ||
45 | *nperm = BLK_PERM_WRITE; | ||
46 | } else { | ||
47 | /* Source child */ | ||
48 | @@ -XXX,XX +XXX,XX @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, | ||
49 | if (perm & BLK_PERM_WRITE) { | ||
50 | *nperm = *nperm | BLK_PERM_CONSISTENT_READ; | ||
51 | } | ||
52 | - *nshared &= ~BLK_PERM_WRITE; | ||
53 | + *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | ||
58 | { | ||
59 | Error *local_err = NULL; | ||
60 | BDRVBackupTopState *state; | ||
61 | - BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter, | ||
62 | - filter_node_name, | ||
63 | - BDRV_O_RDWR, errp); | ||
64 | + BlockDriverState *top; | ||
65 | bool appended = false; | ||
66 | |||
67 | + assert(source->total_sectors == target->total_sectors); | ||
68 | + | ||
69 | + top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name, | ||
70 | + BDRV_O_RDWR, errp); | ||
71 | if (!top) { | ||
72 | return NULL; | ||
73 | } | ||
74 | diff --git a/block/backup.c b/block/backup.c | ||
75 | index XXXXXXX..XXXXXXX 100644 | ||
76 | --- a/block/backup.c | ||
77 | +++ b/block/backup.c | ||
78 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
79 | BlockCompletionFunc *cb, void *opaque, | ||
80 | JobTxn *txn, Error **errp) | ||
81 | { | ||
82 | - int64_t len; | ||
83 | + int64_t len, target_len; | ||
84 | BackupBlockJob *job = NULL; | ||
85 | int64_t cluster_size; | ||
86 | BdrvRequestFlags write_flags; | ||
87 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
88 | goto error; | ||
89 | } | ||
90 | |||
91 | + target_len = bdrv_getlength(target); | ||
92 | + if (target_len < 0) { | ||
93 | + error_setg_errno(errp, -target_len, "Unable to get length for '%s'", | ||
94 | + bdrv_get_device_or_node_name(bs)); | ||
95 | + goto error; | ||
96 | + } | ||
97 | + | ||
98 | + if (target_len != len) { | ||
99 | + error_setg(errp, "Source and target image have different sizes"); | ||
100 | + goto error; | ||
101 | + } | ||
102 | + | ||
103 | cluster_size = backup_calculate_cluster_size(target, errp); | ||
104 | if (cluster_size < 0) { | ||
105 | goto error; | ||
106 | -- | ||
107 | 2.25.3 | ||
108 | |||
109 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | This tests that the backup job catches situations where the target node | ||
2 | has a different size than the source node. It must also forbid resize | ||
3 | operations when the job is already running. | ||
1 | 4 | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
6 | Message-Id: <20200430142755.315494-5-kwolf@redhat.com> | ||
7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
9 | --- | ||
10 | tests/qemu-iotests/055 | 41 ++++++++++++++++++++++++++++++++++++-- | ||
11 | tests/qemu-iotests/055.out | 4 ++-- | ||
12 | 2 files changed, 41 insertions(+), 4 deletions(-) | ||
13 | |||
14 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 | ||
15 | index XXXXXXX..XXXXXXX 100755 | ||
16 | --- a/tests/qemu-iotests/055 | ||
17 | +++ b/tests/qemu-iotests/055 | ||
18 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): | ||
19 | def setUp(self): | ||
20 | qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len)) | ||
21 | |||
22 | - self.vm = iotests.VM().add_drive('blkdebug::' + test_img) | ||
23 | - self.vm.add_drive(blockdev_target_img, interface="none") | ||
24 | + self.vm = iotests.VM() | ||
25 | + self.vm.add_drive('blkdebug::' + test_img, 'node-name=source') | ||
26 | + self.vm.add_drive(blockdev_target_img, 'node-name=target', | ||
27 | + interface="none") | ||
28 | if iotests.qemu_default_machine == 'pc': | ||
29 | self.vm.add_drive(None, 'media=cdrom', 'ide') | ||
30 | self.vm.launch() | ||
31 | @@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase): | ||
32 | def test_pause_blockdev_backup(self): | ||
33 | self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) | ||
34 | |||
35 | + def do_test_resize_blockdev_backup(self, device, node): | ||
36 | + def pre_finalize(): | ||
37 | + result = self.vm.qmp('block_resize', device=device, size=65536) | ||
38 | + self.assert_qmp(result, 'error/class', 'GenericError') | ||
39 | + | ||
40 | + result = self.vm.qmp('block_resize', node_name=node, size=65536) | ||
41 | + self.assert_qmp(result, 'error/class', 'GenericError') | ||
42 | + | ||
43 | + result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', | ||
44 | + target='drive1', sync='full', auto_finalize=False, | ||
45 | + auto_dismiss=False) | ||
46 | + self.assert_qmp(result, 'return', {}) | ||
47 | + | ||
48 | + self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize) | ||
49 | + | ||
50 | + def test_source_resize_blockdev_backup(self): | ||
51 | + self.do_test_resize_blockdev_backup('drive0', 'source') | ||
52 | + | ||
53 | + def test_target_resize_blockdev_backup(self): | ||
54 | + self.do_test_resize_blockdev_backup('drive1', 'target') | ||
55 | + | ||
56 | + def do_test_target_size(self, size): | ||
57 | + result = self.vm.qmp('block_resize', device='drive1', size=size) | ||
58 | + self.assert_qmp(result, 'return', {}) | ||
59 | + | ||
60 | + result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0', | ||
61 | + target='drive1', sync='full') | ||
62 | + self.assert_qmp(result, 'error/class', 'GenericError') | ||
63 | + | ||
64 | + def test_small_target(self): | ||
65 | + self.do_test_target_size(image_len // 2) | ||
66 | + | ||
67 | + def test_large_target(self): | ||
68 | + self.do_test_target_size(image_len * 2) | ||
69 | + | ||
70 | def test_medium_not_found(self): | ||
71 | if iotests.qemu_default_machine != 'pc': | ||
72 | return | ||
73 | diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out | ||
74 | index XXXXXXX..XXXXXXX 100644 | ||
75 | --- a/tests/qemu-iotests/055.out | ||
76 | +++ b/tests/qemu-iotests/055.out | ||
77 | @@ -XXX,XX +XXX,XX @@ | ||
78 | -.................................... | ||
79 | +........................................ | ||
80 | ---------------------------------------------------------------------- | ||
81 | -Ran 36 tests | ||
82 | +Ran 40 tests | ||
83 | |||
84 | OK | ||
85 | -- | ||
86 | 2.25.3 | ||
87 | |||
88 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | 055 uses the backup block job to create a compressed backup of an | ||
2 | $IMGFMT image with both qcow2 and vmdk targets. However, cluster | ||
3 | allocation in vmdk is very slow because it flushes the image file after | ||
4 | each L2 update. | ||
1 | 5 | ||
6 | There is no reason why we need this level of safety in this test, so | ||
7 | let's disable flushes for vmdk. For the blockdev-backup tests this is | ||
8 | achieved by simply adding the cache.no-flush=on to the drive_add() for | ||
9 | the target. For drive-backup, the caching flags are copied from the | ||
10 | source node, so we'll also add the flag to the source node, even though | ||
11 | it is not vmdk. | ||
12 | |||
13 | This can make the test run significantly faster (though it doesn't make | ||
14 | a difference on tmpfs). In my usual setup it goes from ~45s to ~15s. | ||
15 | |||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | Message-Id: <20200505064618.16267-1-kwolf@redhat.com> | ||
18 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
20 | --- | ||
21 | tests/qemu-iotests/055 | 11 +++++++---- | ||
22 | 1 file changed, 7 insertions(+), 4 deletions(-) | ||
23 | |||
24 | diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 | ||
25 | index XXXXXXX..XXXXXXX 100755 | ||
26 | --- a/tests/qemu-iotests/055 | ||
27 | +++ b/tests/qemu-iotests/055 | ||
28 | @@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase): | ||
29 | |||
30 | class TestCompressedToQcow2(iotests.QMPTestCase): | ||
31 | image_len = 64 * 1024 * 1024 # MB | ||
32 | - target_fmt = {'type': 'qcow2', 'args': ()} | ||
33 | + target_fmt = {'type': 'qcow2', 'args': (), 'drive-opts': ''} | ||
34 | |||
35 | def tearDown(self): | ||
36 | self.vm.shutdown() | ||
37 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): | ||
38 | pass | ||
39 | |||
40 | def do_prepare_drives(self, attach_target): | ||
41 | - self.vm = iotests.VM().add_drive('blkdebug::' + test_img) | ||
42 | + self.vm = iotests.VM().add_drive('blkdebug::' + test_img, | ||
43 | + opts=self.target_fmt['drive-opts']) | ||
44 | |||
45 | qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img, | ||
46 | str(self.image_len), *self.target_fmt['args']) | ||
47 | if attach_target: | ||
48 | self.vm.add_drive(blockdev_target_img, | ||
49 | img_format=self.target_fmt['type'], | ||
50 | - interface="none") | ||
51 | + interface="none", | ||
52 | + opts=self.target_fmt['drive-opts']) | ||
53 | |||
54 | self.vm.launch() | ||
55 | |||
56 | @@ -XXX,XX +XXX,XX @@ class TestCompressedToQcow2(iotests.QMPTestCase): | ||
57 | |||
58 | |||
59 | class TestCompressedToVmdk(TestCompressedToQcow2): | ||
60 | - target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')} | ||
61 | + target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized'), | ||
62 | + 'drive-opts': 'cache.no-flush=on'} | ||
63 | |||
64 | @iotests.skip_if_unsupported(['vmdk']) | ||
65 | def setUp(self): | ||
66 | -- | ||
67 | 2.25.3 | ||
68 | |||
69 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | From: Max Reitz <mreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Calling bdrv_getlength() to get the pre-truncate file size will not | ||
4 | really work on block devices, because they have always the same length, | ||
5 | and trying to write beyond it will fail with a rather cryptic error | ||
6 | message. | ||
7 | |||
8 | Instead, we should use qcow2_get_last_cluster() and bdrv_getlength() | ||
9 | only as a fallback. | ||
10 | |||
11 | Before this patch: | ||
12 | $ truncate -s 1G test.img | ||
13 | $ sudo losetup -f --show test.img | ||
14 | /dev/loop0 | ||
15 | $ sudo qemu-img create -f qcow2 -o preallocation=full /dev/loop0 64M | ||
16 | Formatting '/dev/loop0', fmt=qcow2 size=67108864 cluster_size=65536 | ||
17 | preallocation=full lazy_refcounts=off refcount_bits=16 | ||
18 | qemu-img: /dev/loop0: Could not resize image: Failed to resize refcount | ||
19 | structures: No space left on device | ||
20 | |||
21 | With this patch: | ||
22 | $ sudo qemu-img create -f qcow2 -o preallocation=full /dev/loop0 64M | ||
23 | Formatting '/dev/loop0', fmt=qcow2 size=67108864 cluster_size=65536 | ||
24 | preallocation=full lazy_refcounts=off refcount_bits=16 | ||
25 | qemu-img: /dev/loop0: Could not resize image: Failed to resize | ||
26 | underlying file: Preallocation mode 'full' unsupported for this | ||
27 | non-regular file | ||
28 | |||
29 | So as you can see, it still fails, but now the problem is missing | ||
30 | support on the block device level, so we at least get a better error | ||
31 | message. | ||
32 | |||
33 | Note that we cannot preallocate block devices on truncate by design, | ||
34 | because we do not know what area to preallocate. Their length is always | ||
35 | the same, the truncate operation does not change it. | ||
36 | |||
3 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 37 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
4 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 38 | Message-Id: <20200505141801.1096763-1-mreitz@redhat.com> |
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 39 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | --- | 40 | --- |
7 | tests/qemu-iotests/182 | 71 ++++++++++++++++++++++++++++++++++++++ | 41 | block/qcow2.c | 10 ++++++++-- |
8 | tests/qemu-iotests/182.out | 9 +++++ | 42 | 1 file changed, 8 insertions(+), 2 deletions(-) |
9 | 2 files changed, 80 insertions(+) | ||
10 | 43 | ||
11 | diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 | 44 | diff --git a/block/qcow2.c b/block/qcow2.c |
12 | index XXXXXXX..XXXXXXX 100755 | 45 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/tests/qemu-iotests/182 | 46 | --- a/block/qcow2.c |
14 | +++ b/tests/qemu-iotests/182 | 47 | +++ b/block/qcow2.c |
15 | @@ -XXX,XX +XXX,XX @@ status=1 # failure is the default! | 48 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, |
16 | _cleanup() | 49 | { |
17 | { | 50 | int64_t allocation_start, host_offset, guest_offset; |
18 | _cleanup_test_img | 51 | int64_t clusters_allocated; |
19 | + rm -f "$TEST_IMG.overlay" | 52 | - int64_t old_file_size, new_file_size; |
20 | } | 53 | + int64_t old_file_size, last_cluster, new_file_size; |
21 | trap "_cleanup; exit \$status" 0 1 2 3 15 | 54 | uint64_t nb_new_data_clusters, nb_new_l2_tables; |
22 | 55 | ||
23 | @@ -XXX,XX +XXX,XX @@ echo 'quit' | $QEMU -nographic -monitor stdio \ | 56 | /* With a data file, preallocation means just allocating the metadata |
24 | 57 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, | |
25 | _cleanup_qemu | 58 | ret = old_file_size; |
26 | 59 | goto fail; | |
27 | +echo | 60 | } |
28 | +echo '=== Testing reopen ===' | 61 | - old_file_size = ROUND_UP(old_file_size, s->cluster_size); |
29 | +echo | ||
30 | + | 62 | + |
31 | +# This tests that reopening does not unshare any permissions it should | 63 | + last_cluster = qcow2_get_last_cluster(bs, old_file_size); |
32 | +# not unshare | 64 | + if (last_cluster >= 0) { |
33 | +# (There was a bug where reopening shared exactly the opposite of the | 65 | + old_file_size = (last_cluster + 1) * s->cluster_size; |
34 | +# permissions it was supposed to share) | 66 | + } else { |
35 | + | 67 | + old_file_size = ROUND_UP(old_file_size, s->cluster_size); |
36 | +_launch_qemu | 68 | + } |
37 | + | 69 | |
38 | +_send_qemu_cmd $QEMU_HANDLE \ | 70 | nb_new_data_clusters = DIV_ROUND_UP(offset - old_length, |
39 | + "{'execute': 'qmp_capabilities'}" \ | 71 | s->cluster_size); |
40 | + 'return' | ||
41 | + | ||
42 | +# Open the image without any format layer (we are not going to access | ||
43 | +# it, so that is fine) | ||
44 | +# This should keep all permissions shared. | ||
45 | +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ | ||
46 | + "{'execute': 'blockdev-add', | ||
47 | + 'arguments': { | ||
48 | + 'node-name': 'node0', | ||
49 | + 'driver': 'file', | ||
50 | + 'filename': '$TEST_IMG', | ||
51 | + 'locking': 'on' | ||
52 | + } }" \ | ||
53 | + 'return' \ | ||
54 | + 'error' | ||
55 | + | ||
56 | +# This snapshot will perform a reopen to drop R/W to RO. | ||
57 | +# It should still keep all permissions shared. | ||
58 | +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ | ||
59 | + "{'execute': 'blockdev-snapshot-sync', | ||
60 | + 'arguments': { | ||
61 | + 'node-name': 'node0', | ||
62 | + 'snapshot-file': '$TEST_IMG.overlay', | ||
63 | + 'snapshot-node-name': 'node1' | ||
64 | + } }" \ | ||
65 | + 'return' \ | ||
66 | + 'error' | ||
67 | + | ||
68 | +# Now open the same file again | ||
69 | +# This does not require any permissions (and does not unshare any), so | ||
70 | +# this will not conflict with node0. | ||
71 | +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ | ||
72 | + "{'execute': 'blockdev-add', | ||
73 | + 'arguments': { | ||
74 | + 'node-name': 'node1', | ||
75 | + 'driver': 'file', | ||
76 | + 'filename': '$TEST_IMG', | ||
77 | + 'locking': 'on' | ||
78 | + } }" \ | ||
79 | + 'return' \ | ||
80 | + 'error' | ||
81 | + | ||
82 | +# Now we attach the image to a virtio-blk device. This device does | ||
83 | +# require some permissions (at least WRITE and READ_CONSISTENT), so if | ||
84 | +# reopening node0 unshared any (which it should not have), this will | ||
85 | +# fail (but it should not). | ||
86 | +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ | ||
87 | + "{'execute': 'device_add', | ||
88 | + 'arguments': { | ||
89 | + 'driver': 'virtio-blk', | ||
90 | + 'drive': 'node1' | ||
91 | + } }" \ | ||
92 | + 'return' \ | ||
93 | + 'error' | ||
94 | + | ||
95 | +_cleanup_qemu | ||
96 | + | ||
97 | # success, all done | ||
98 | echo "*** done" | ||
99 | rm -f $seq.full | ||
100 | diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out | ||
101 | index XXXXXXX..XXXXXXX 100644 | ||
102 | --- a/tests/qemu-iotests/182.out | ||
103 | +++ b/tests/qemu-iotests/182.out | ||
104 | @@ -XXX,XX +XXX,XX @@ Starting QEMU | ||
105 | Starting a second QEMU using the same image should fail | ||
106 | QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock | ||
107 | Is another process using the image [TEST_DIR/t.qcow2]? | ||
108 | + | ||
109 | +=== Testing reopen === | ||
110 | + | ||
111 | +{"return": {}} | ||
112 | +{"return": {}} | ||
113 | +Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16 | ||
114 | +{"return": {}} | ||
115 | +{"return": {}} | ||
116 | +{"return": {}} | ||
117 | *** done | ||
118 | -- | 72 | -- |
119 | 2.19.1 | 73 | 2.25.3 |
120 | 74 | ||
121 | 75 | diff view generated by jsdifflib |
1 | From: Li Qiang <liq3ea@gmail.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Currently, the nvme_cmb_ops mr doesn't check the addr and size. | 3 | block.c already defaults to 0 if we don't provide a callback; there's |
4 | This can lead an oob access issue. This is triggerable in the guest. | 4 | no need to write a callback that always fails. |
5 | Add check to avoid this issue. | ||
6 | 5 | ||
7 | Fixes CVE-2018-16847. | 6 | Signed-off-by: Eric Blake <eblake@redhat.com> |
8 | 7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | |
9 | Reported-by: Li Qiang <liq3ea@gmail.com> | 8 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
10 | Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> | 9 | Message-Id: <20200428202905.770727-2-eblake@redhat.com> |
11 | Signed-off-by: Li Qiang <liq3ea@gmail.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 11 | --- |
14 | hw/block/nvme.c | 7 +++++++ | 12 | block/gluster.c | 14 -------------- |
15 | 1 file changed, 7 insertions(+) | 13 | 1 file changed, 14 deletions(-) |
16 | 14 | ||
17 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | 15 | diff --git a/block/gluster.c b/block/gluster.c |
18 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/hw/block/nvme.c | 17 | --- a/block/gluster.c |
20 | +++ b/hw/block/nvme.c | 18 | +++ b/block/gluster.c |
21 | @@ -XXX,XX +XXX,XX @@ static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data, | 19 | @@ -XXX,XX +XXX,XX @@ static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs) |
22 | unsigned size) | 20 | } |
23 | { | ||
24 | NvmeCtrl *n = (NvmeCtrl *)opaque; | ||
25 | + | ||
26 | + if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) { | ||
27 | + return; | ||
28 | + } | ||
29 | memcpy(&n->cmbuf[addr], &data, size); | ||
30 | } | 21 | } |
31 | 22 | ||
32 | @@ -XXX,XX +XXX,XX @@ static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size) | 23 | -static int qemu_gluster_has_zero_init(BlockDriverState *bs) |
33 | uint64_t val; | 24 | -{ |
34 | NvmeCtrl *n = (NvmeCtrl *)opaque; | 25 | - /* GlusterFS volume could be backed by a block device */ |
35 | 26 | - return 0; | |
36 | + if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) { | 27 | -} |
37 | + return 0; | 28 | - |
38 | + } | 29 | /* |
39 | memcpy(&val, &n->cmbuf[addr], size); | 30 | * Find allocation range in @bs around offset @start. |
40 | return val; | 31 | * May change underlying file descriptor's file offset. |
41 | } | 32 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster = { |
33 | .bdrv_co_readv = qemu_gluster_co_readv, | ||
34 | .bdrv_co_writev = qemu_gluster_co_writev, | ||
35 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, | ||
36 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, | ||
37 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, | ||
38 | #ifdef CONFIG_GLUSTERFS_DISCARD | ||
39 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, | ||
40 | #endif | ||
41 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_tcp = { | ||
42 | .bdrv_co_readv = qemu_gluster_co_readv, | ||
43 | .bdrv_co_writev = qemu_gluster_co_writev, | ||
44 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, | ||
45 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, | ||
46 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, | ||
47 | #ifdef CONFIG_GLUSTERFS_DISCARD | ||
48 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, | ||
49 | #endif | ||
50 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_unix = { | ||
51 | .bdrv_co_readv = qemu_gluster_co_readv, | ||
52 | .bdrv_co_writev = qemu_gluster_co_writev, | ||
53 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, | ||
54 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, | ||
55 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, | ||
56 | #ifdef CONFIG_GLUSTERFS_DISCARD | ||
57 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, | ||
58 | #endif | ||
59 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_rdma = { | ||
60 | .bdrv_co_readv = qemu_gluster_co_readv, | ||
61 | .bdrv_co_writev = qemu_gluster_co_writev, | ||
62 | .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, | ||
63 | - .bdrv_has_zero_init = qemu_gluster_has_zero_init, | ||
64 | - .bdrv_has_zero_init_truncate = qemu_gluster_has_zero_init, | ||
65 | #ifdef CONFIG_GLUSTERFS_DISCARD | ||
66 | .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, | ||
67 | #endif | ||
42 | -- | 68 | -- |
43 | 2.19.1 | 69 | 2.25.3 |
44 | 70 | ||
45 | 71 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | When using bdrv_file, .bdrv_has_zero_init_truncate always returns 1; | ||
4 | therefore, we can behave just like file-posix, and always implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it since the OS gives it to us for | ||
6 | free (note that file-posix.c had to use an 'if' because it shared code | ||
7 | between regular files and block devices, but in file-win32.c, | ||
8 | bdrv_host_device uses a separate .bdrv_file_open). | ||
9 | |||
10 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
11 | Message-Id: <20200428202905.770727-3-eblake@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | block/file-win32.c | 3 +++ | ||
15 | 1 file changed, 3 insertions(+) | ||
16 | |||
17 | diff --git a/block/file-win32.c b/block/file-win32.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/file-win32.c | ||
20 | +++ b/block/file-win32.c | ||
21 | @@ -XXX,XX +XXX,XX @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, | ||
22 | win32_aio_attach_aio_context(s->aio, bdrv_get_aio_context(bs)); | ||
23 | } | ||
24 | |||
25 | + /* When extending regular files, we get zeros from the OS */ | ||
26 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; | ||
27 | + | ||
28 | ret = 0; | ||
29 | fail: | ||
30 | qemu_opts_del(opts); | ||
31 | -- | ||
32 | 2.25.3 | ||
33 | |||
34 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Our code was already checking that we did not attempt to | 3 | Our .bdrv_has_zero_init_truncate returns 1 if we detect that the OS |
4 | allocate more clusters than what would fit in an INT64 (the | 4 | always 0-fills; we can use that same knowledge to implement |
5 | physical maximimum if we can access a full off_t's worth of | 5 | BDRV_REQ_ZERO_WRITE by ignoring it when the OS gives it to us for |
6 | data). But this does not catch smaller limits enforced by | 6 | free. |
7 | various spots in the qcow2 image description: L1 and normal | ||
8 | clusters of L2 are documented as having bits 63-56 reserved | ||
9 | for other purposes, capping our maximum offset at 64PB (bit | ||
10 | 55 is the maximum bit set). And for compressed images with | ||
11 | 2M clusters, the cap drops the maximum offset to bit 48, or | ||
12 | a maximum offset of 512TB. If we overflow that offset, we | ||
13 | would write compressed data into one place, but try to | ||
14 | decompress from another, which won't work. | ||
15 | |||
16 | It's actually possible to prove that overflow can cause image | ||
17 | corruption without this patch; I'll add the iotests separately | ||
18 | in the next commit. | ||
19 | 7 | ||
20 | Signed-off-by: Eric Blake <eblake@redhat.com> | 8 | Signed-off-by: Eric Blake <eblake@redhat.com> |
21 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 9 | Message-Id: <20200428202905.770727-4-eblake@redhat.com> |
22 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
23 | --- | 11 | --- |
24 | block/qcow2.h | 6 ++++++ | 12 | block/nfs.c | 3 +++ |
25 | block/qcow2-refcount.c | 20 +++++++++++++------- | 13 | 1 file changed, 3 insertions(+) |
26 | 2 files changed, 19 insertions(+), 7 deletions(-) | ||
27 | 14 | ||
28 | diff --git a/block/qcow2.h b/block/qcow2.h | 15 | diff --git a/block/nfs.c b/block/nfs.c |
29 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/block/qcow2.h | 17 | --- a/block/nfs.c |
31 | +++ b/block/qcow2.h | 18 | +++ b/block/nfs.c |
32 | @@ -XXX,XX +XXX,XX @@ | 19 | @@ -XXX,XX +XXX,XX @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, |
33 | #define QCOW_MAX_CRYPT_CLUSTERS 32 | ||
34 | #define QCOW_MAX_SNAPSHOTS 65536 | ||
35 | |||
36 | +/* Field widths in qcow2 mean normal cluster offsets cannot reach | ||
37 | + * 64PB; depending on cluster size, compressed clusters can have a | ||
38 | + * smaller limit (64PB for up to 16k clusters, then ramps down to | ||
39 | + * 512TB for 2M clusters). */ | ||
40 | +#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) | ||
41 | + | ||
42 | /* 8 MB refcount table is enough for 2 PB images at 64k cluster size | ||
43 | * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ | ||
44 | #define QCOW_MAX_REFTABLE_SIZE S_8MiB | ||
45 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
46 | index XXXXXXX..XXXXXXX 100644 | ||
47 | --- a/block/qcow2-refcount.c | ||
48 | +++ b/block/qcow2-refcount.c | ||
49 | @@ -XXX,XX +XXX,XX @@ | ||
50 | #include "qemu/bswap.h" | ||
51 | #include "qemu/cutils.h" | ||
52 | |||
53 | -static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); | ||
54 | +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | ||
55 | + uint64_t max); | ||
56 | static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, | ||
57 | int64_t offset, int64_t length, uint64_t addend, | ||
58 | bool decrease, enum qcow2_discard_type type); | ||
59 | @@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs, | ||
60 | } | 20 | } |
61 | 21 | ||
62 | /* Allocate the refcount block itself and mark it as used */ | 22 | bs->total_sectors = ret; |
63 | - int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); | 23 | + if (client->has_zero_init) { |
64 | + int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); | 24 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; |
65 | if (new_block < 0) { | 25 | + } |
66 | return new_block; | 26 | return 0; |
67 | } | 27 | } |
68 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, | 28 | |
69 | |||
70 | |||
71 | /* return < 0 if error */ | ||
72 | -static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) | ||
73 | +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, | ||
74 | + uint64_t max) | ||
75 | { | ||
76 | BDRVQcow2State *s = bs->opaque; | ||
77 | uint64_t i, nb_clusters, refcount; | ||
78 | @@ -XXX,XX +XXX,XX @@ retry: | ||
79 | } | ||
80 | |||
81 | /* Make sure that all offsets in the "allocated" range are representable | ||
82 | - * in an int64_t */ | ||
83 | + * in the requested max */ | ||
84 | if (s->free_cluster_index > 0 && | ||
85 | - s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) | ||
86 | + s->free_cluster_index - 1 > (max >> s->cluster_bits)) | ||
87 | { | ||
88 | return -EFBIG; | ||
89 | } | ||
90 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) | ||
91 | |||
92 | BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); | ||
93 | do { | ||
94 | - offset = alloc_clusters_noref(bs, size); | ||
95 | + offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); | ||
96 | if (offset < 0) { | ||
97 | return offset; | ||
98 | } | ||
99 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) | ||
100 | free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); | ||
101 | do { | ||
102 | if (!offset || free_in_cluster < size) { | ||
103 | - int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); | ||
104 | + int64_t new_cluster; | ||
105 | + | ||
106 | + new_cluster = alloc_clusters_noref(bs, s->cluster_size, | ||
107 | + MIN(s->cluster_offset_mask, | ||
108 | + QCOW_MAX_CLUSTER_OFFSET)); | ||
109 | if (new_cluster < 0) { | ||
110 | return new_cluster; | ||
111 | } | ||
112 | -- | 29 | -- |
113 | 2.19.1 | 30 | 2.25.3 |
114 | 31 | ||
115 | 32 | diff view generated by jsdifflib |
1 | Don't leak 'cluster' in the mapping == NULL case. Found by Coverity | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | (CID 1055918). | ||
3 | 2 | ||
4 | Fixes: 8d9401c2791ee2d2805b741b1ee3006041edcd3e | 3 | Our .bdrv_has_zero_init_truncate always returns 1 because rbd always |
4 | 0-fills; we can use that same knowledge to implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it. | ||
6 | |||
7 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
8 | Message-Id: <20200428202905.770727-5-eblake@redhat.com> | ||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
7 | Reviewed-by: Liam Merwick <liam.merwick@oracle.com> | ||
8 | Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
9 | --- | 10 | --- |
10 | block/vvfat.c | 6 +++--- | 11 | block/rbd.c | 3 +++ |
11 | 1 file changed, 3 insertions(+), 3 deletions(-) | 12 | 1 file changed, 3 insertions(+) |
12 | 13 | ||
13 | diff --git a/block/vvfat.c b/block/vvfat.c | 14 | diff --git a/block/rbd.c b/block/rbd.c |
14 | index XXXXXXX..XXXXXXX 100644 | 15 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/block/vvfat.c | 16 | --- a/block/rbd.c |
16 | +++ b/block/vvfat.c | 17 | +++ b/block/rbd.c |
17 | @@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s, | 18 | @@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, |
18 | uint32_t first_cluster = c; | ||
19 | mapping_t* mapping = find_mapping_for_cluster(s, c); | ||
20 | uint32_t size = filesize_of_direntry(direntry); | ||
21 | - char* cluster = g_malloc(s->cluster_size); | ||
22 | + char *cluster; | ||
23 | uint32_t i; | ||
24 | int fd = 0; | ||
25 | |||
26 | @@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s, | ||
27 | if (fd < 0) { | ||
28 | fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, | ||
29 | strerror(errno), errno); | ||
30 | - g_free(cluster); | ||
31 | return fd; | ||
32 | } | ||
33 | if (offset > 0) { | ||
34 | if (lseek(fd, offset, SEEK_SET) != offset) { | ||
35 | qemu_close(fd); | ||
36 | - g_free(cluster); | ||
37 | return -3; | ||
38 | } | 19 | } |
39 | } | 20 | } |
40 | 21 | ||
41 | + cluster = g_malloc(s->cluster_size); | 22 | + /* When extending regular files, we get zeros from the OS */ |
23 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; | ||
42 | + | 24 | + |
43 | while (offset < size) { | 25 | r = 0; |
44 | uint32_t c1; | 26 | goto out; |
45 | int rest_size = (size - offset > s->cluster_size ? | 27 | |
46 | -- | 28 | -- |
47 | 2.19.1 | 29 | 2.25.3 |
48 | 30 | ||
49 | 31 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | Our .bdrv_has_zero_init_truncate always returns 1 because sheepdog | ||
4 | always 0-fills; we can use that same knowledge to implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it. | ||
6 | |||
7 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
8 | Message-Id: <20200428202905.770727-6-eblake@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | --- | ||
11 | block/sheepdog.c | 1 + | ||
12 | 1 file changed, 1 insertion(+) | ||
13 | |||
14 | diff --git a/block/sheepdog.c b/block/sheepdog.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/sheepdog.c | ||
17 | +++ b/block/sheepdog.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, | ||
19 | memcpy(&s->inode, buf, sizeof(s->inode)); | ||
20 | |||
21 | bs->total_sectors = s->inode.vdi_size / BDRV_SECTOR_SIZE; | ||
22 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; | ||
23 | pstrcpy(s->name, sizeof(s->name), vdi); | ||
24 | qemu_co_mutex_init(&s->lock); | ||
25 | qemu_co_mutex_init(&s->queue_lock); | ||
26 | -- | ||
27 | 2.25.3 | ||
28 | |||
29 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | Our .bdrv_has_zero_init_truncate can detect when the remote side | ||
4 | always zero fills; we can reuse that same knowledge to implement | ||
5 | BDRV_REQ_ZERO_WRITE by ignoring it when the server gives it to us for | ||
6 | free. | ||
7 | |||
8 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
9 | Message-Id: <20200428202905.770727-7-eblake@redhat.com> | ||
10 | Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | --- | ||
13 | block/ssh.c | 4 ++++ | ||
14 | 1 file changed, 4 insertions(+) | ||
15 | |||
16 | diff --git a/block/ssh.c b/block/ssh.c | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/block/ssh.c | ||
19 | +++ b/block/ssh.c | ||
20 | @@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, | ||
21 | /* Go non-blocking. */ | ||
22 | ssh_set_blocking(s->session, 0); | ||
23 | |||
24 | + if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) { | ||
25 | + bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; | ||
26 | + } | ||
27 | + | ||
28 | qapi_free_BlockdevOptionsSsh(opts); | ||
29 | |||
30 | return 0; | ||
31 | -- | ||
32 | 2.25.3 | ||
33 | |||
34 | diff view generated by jsdifflib |
1 | From: Eric Blake <eblake@redhat.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Although off_t permits up to 63 bits (8EB) of file offsets, in | 3 | The parallels driver tries to use truncation for image growth, but can |
4 | practice, we're going to hit other limits first. Document some | 4 | only do so when reads are guaranteed as zero. Now that we have a way |
5 | of those limits in the qcow2 spec (some are inherent, others are | 5 | to request zero contents from truncation, we can defer the decision to |
6 | implementation choices of qemu), and how choice of cluster size | 6 | actual allocation attempts rather than up front, reducing the number |
7 | can influence some of the limits. | 7 | of places that still use bdrv_has_zero_init_truncate. |
8 | |||
9 | While we cannot map any uncompressed virtual cluster to any | ||
10 | address higher than 64 PB (56 bits) (due to the current L1/L2 | ||
11 | field encoding stopping at bit 55), qemu's cap of 8M for the | ||
12 | refcount table can still access larger host addresses for some | ||
13 | combinations of large clusters and small refcount_order. For | ||
14 | comparison, ext4 with 4k blocks caps files at 16PB. | ||
15 | |||
16 | Another interesting limit: for compressed clusters, the L2 layout | ||
17 | requires an ever-smaller maximum host offset as cluster size gets | ||
18 | larger, down to a 512 TB maximum with 2M clusters. In particular, | ||
19 | note that with a cluster size of 8k or smaller, the L2 entry for | ||
20 | a compressed cluster could technically point beyond the 64PB mark, | ||
21 | but when you consider that with 8k clusters and refcount_order = 0, | ||
22 | you cannot access beyond 512T without exceeding qemu's limit of an | ||
23 | 8M cap on the refcount table, it is unlikely that any image in the | ||
24 | wild has attempted to do so. To be safe, let's document that bits | ||
25 | beyond 55 in a compressed cluster must be 0. | ||
26 | 8 | ||
27 | Signed-off-by: Eric Blake <eblake@redhat.com> | 9 | Signed-off-by: Eric Blake <eblake@redhat.com> |
10 | Message-Id: <20200428202905.770727-8-eblake@redhat.com> | ||
11 | Reviewed-by: Denis V. Lunev <den@openvz.org> | ||
28 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
29 | --- | 13 | --- |
30 | docs/interop/qcow2.txt | 38 ++++++++++++++++++++++++++++++++++++-- | 14 | block/parallels.c | 25 ++++++++++++++++--------- |
31 | 1 file changed, 36 insertions(+), 2 deletions(-) | 15 | 1 file changed, 16 insertions(+), 9 deletions(-) |
32 | 16 | ||
33 | diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt | 17 | diff --git a/block/parallels.c b/block/parallels.c |
34 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
35 | --- a/docs/interop/qcow2.txt | 19 | --- a/block/parallels.c |
36 | +++ b/docs/interop/qcow2.txt | 20 | +++ b/block/parallels.c |
37 | @@ -XXX,XX +XXX,XX @@ The first cluster of a qcow2 image contains the file header: | 21 | @@ -XXX,XX +XXX,XX @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, |
38 | with larger cluster sizes. | 22 | static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, |
39 | 23 | int nb_sectors, int *pnum) | |
40 | 24 - 31: size | 24 | { |
41 | - Virtual disk size in bytes | 25 | - int ret; |
42 | + Virtual disk size in bytes. | 26 | + int ret = 0; |
43 | + | 27 | BDRVParallelsState *s = bs->opaque; |
44 | + Note: qemu has an implementation limit of 32 MB as | 28 | int64_t pos, space, idx, to_allocate, i, len; |
45 | + the maximum L1 table size. With a 2 MB cluster | 29 | |
46 | + size, it is unable to populate a virtual cluster | 30 | @@ -XXX,XX +XXX,XX @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, |
47 | + beyond 2 EB (61 bits); with a 512 byte cluster | 31 | } |
48 | + size, it is unable to populate a virtual size | 32 | if (s->data_end + space > (len >> BDRV_SECTOR_BITS)) { |
49 | + larger than 128 GB (37 bits). Meanwhile, L1/L2 | 33 | space += s->prealloc_size; |
50 | + table layouts limit an image to no more than 64 PB | 34 | + /* |
51 | + (56 bits) of populated clusters, and an image may | 35 | + * We require the expanded size to read back as zero. If the |
52 | + hit other limits first (such as a file system's | 36 | + * user permitted truncation, we try that; but if it fails, we |
53 | + maximum size). | 37 | + * force the safer-but-slower fallocate. |
54 | 38 | + */ | |
55 | 32 - 35: crypt_method | 39 | + if (s->prealloc_mode == PRL_PREALLOC_MODE_TRUNCATE) { |
56 | 0 for no encryption | 40 | + ret = bdrv_truncate(bs->file, |
57 | @@ -XXX,XX +XXX,XX @@ in the image file. | 41 | + (s->data_end + space) << BDRV_SECTOR_BITS, |
58 | It contains pointers to the second level structures which are called refcount | 42 | + false, PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, |
59 | blocks and are exactly one cluster in size. | 43 | + NULL); |
60 | 44 | + if (ret == -ENOTSUP) { | |
61 | +Although a large enough refcount table can reserve clusters past 64 PB | 45 | + s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; |
62 | +(56 bits) (assuming the underlying protocol can even be sized that | 46 | + } |
63 | +large), note that some qcow2 metadata such as L1/L2 tables must point | 47 | + } |
64 | +to clusters prior to that point. | 48 | if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { |
65 | + | 49 | ret = bdrv_pwrite_zeroes(bs->file, |
66 | +Note: qemu has an implementation limit of 8 MB as the maximum refcount | 50 | s->data_end << BDRV_SECTOR_BITS, |
67 | +table size. With a 2 MB cluster size and a default refcount_order of | 51 | space << BDRV_SECTOR_BITS, 0); |
68 | +4, it is unable to reference host resources beyond 2 EB (61 bits); in | 52 | - } else { |
69 | +the worst case, with a 512 cluster size and refcount_order of 6, it is | 53 | - ret = bdrv_truncate(bs->file, |
70 | +unable to access beyond 32 GB (35 bits). | 54 | - (s->data_end + space) << BDRV_SECTOR_BITS, |
71 | + | 55 | - false, PREALLOC_MODE_OFF, 0, NULL); |
72 | Given an offset into the image file, the refcount of its cluster can be | 56 | } |
73 | obtained as follows: | 57 | if (ret < 0) { |
74 | 58 | return ret; | |
75 | @@ -XXX,XX +XXX,XX @@ The L1 table has a variable size (stored in the header) and may use multiple | 59 | @@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, |
76 | clusters, however it must be contiguous in the image file. L2 tables are | 60 | qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0); |
77 | exactly one cluster in size. | 61 | s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS); |
78 | 62 | buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE); | |
79 | +The L1 and L2 tables have implications on the maximum virtual file | 63 | + /* prealloc_mode can be downgraded later during allocate_clusters */ |
80 | +size; for a given L1 table size, a larger cluster size is required for | 64 | s->prealloc_mode = qapi_enum_parse(&prealloc_mode_lookup, buf, |
81 | +the guest to have access to more space. Furthermore, a virtual | 65 | PRL_PREALLOC_MODE_FALLOCATE, |
82 | +cluster must currently map to a host offset below 64 PB (56 bits) | 66 | &local_err); |
83 | +(although this limit could be relaxed by putting reserved bits into | 67 | @@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, |
84 | +use). Additionally, as cluster size increases, the maximum host | 68 | goto fail_options; |
85 | +offset for a compressed cluster is reduced (a 2M cluster size requires | 69 | } |
86 | +compressed clusters to reside below 512 TB (49 bits), and this limit | 70 | |
87 | +cannot be relaxed without an incompatible layout change). | 71 | - if (!bdrv_has_zero_init_truncate(bs->file->bs)) { |
88 | + | 72 | - s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; |
89 | Given an offset into the virtual disk, the offset into the image file can be | 73 | - } |
90 | obtained as follows: | 74 | - |
91 | 75 | if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) { | |
92 | @@ -XXX,XX +XXX,XX @@ Standard Cluster Descriptor: | 76 | s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); |
93 | Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): | 77 | ret = parallels_update_header(bs); |
94 | |||
95 | Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a | ||
96 | - cluster or sector boundary! | ||
97 | + cluster or sector boundary! If cluster_bits is | ||
98 | + small enough that this field includes bits beyond | ||
99 | + 55, those upper bits must be set to 0. | ||
100 | |||
101 | x - 61: Number of additional 512-byte sectors used for the | ||
102 | compressed data, beyond the sector containing the offset | ||
103 | -- | 78 | -- |
104 | 2.19.1 | 79 | 2.25.3 |
105 | 80 | ||
106 | 81 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | bdrv_reopen_multiple() does not invoke bdrv_reopen_abort() for the | 3 | The vhdx driver uses truncation for image growth, with a special case |
4 | element of the reopen queue for which bdrv_reopen_prepare() failed, | 4 | for blocks that already read as zero but which are only being |
5 | because it assumes that the prepare function will have rolled back all | 5 | partially written. But with a bit of rearranging, it's just as easy |
6 | changes already. | 6 | to defer the decision on whether truncation resulted in zeroes to the |
7 | actual allocation attempt, reducing the number of places that still | ||
8 | use bdrv_has_zero_init_truncate. | ||
7 | 9 | ||
8 | However, bdrv_reopen_prepare() does not do this in every case: It may | 10 | Signed-off-by: Eric Blake <eblake@redhat.com> |
9 | notice an error after BlockDriver.bdrv_reopen_prepare() succeeded, and | 11 | Message-Id: <20200428202905.770727-9-eblake@redhat.com> |
10 | it will not invoke BlockDriver.bdrv_reopen_abort() then; and neither | ||
11 | will bdrv_reopen_multiple(), as explained above. | ||
12 | |||
13 | This is wrong because we must always call .bdrv_reopen_commit() or | ||
14 | .bdrv_reopen_abort() after .bdrv_reopen_prepare() has succeeded. | ||
15 | Otherwise, the block driver has no chance to undo what it has done in | ||
16 | its implementation of .bdrv_reopen_prepare(). | ||
17 | |||
18 | To fix this, bdrv_reopen_prepare() has to call .bdrv_reopen_abort() if | ||
19 | it wants to return an error after .bdrv_reopen_prepare() has succeeded. | ||
20 | |||
21 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
22 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
24 | --- | 13 | --- |
25 | block.c | 12 ++++++++++++ | 14 | block/vhdx.c | 89 ++++++++++++++++++++++++++++++---------------------- |
26 | 1 file changed, 12 insertions(+) | 15 | 1 file changed, 51 insertions(+), 38 deletions(-) |
27 | 16 | ||
28 | diff --git a/block.c b/block.c | 17 | diff --git a/block/vhdx.c b/block/vhdx.c |
29 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/block.c | 19 | --- a/block/vhdx.c |
31 | +++ b/block.c | 20 | +++ b/block/vhdx.c |
32 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, | 21 | @@ -XXX,XX +XXX,XX @@ exit: |
33 | QDict *orig_reopen_opts; | 22 | /* |
34 | char *discard = NULL; | 23 | * Allocate a new payload block at the end of the file. |
35 | bool read_only; | 24 | * |
36 | + bool drv_prepared = false; | 25 | - * Allocation will happen at 1MB alignment inside the file |
37 | 26 | + * Allocation will happen at 1MB alignment inside the file. | |
38 | assert(reopen_state != NULL); | 27 | + * |
39 | assert(reopen_state->bs->drv != NULL); | 28 | + * If @need_zero is set on entry but not cleared on return, then truncation |
40 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, | 29 | + * could not guarantee that the new portion reads as zero, and the caller |
41 | goto error; | 30 | + * will take care of it instead. |
31 | * | ||
32 | * Returns the file offset start of the new payload block | ||
33 | */ | ||
34 | static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, | ||
35 | - uint64_t *new_offset) | ||
36 | + uint64_t *new_offset, bool *need_zero) | ||
37 | { | ||
38 | int64_t current_len; | ||
39 | |||
40 | @@ -XXX,XX +XXX,XX @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, | ||
41 | return -EINVAL; | ||
42 | } | 42 | } |
43 | 43 | ||
44 | + drv_prepared = true; | 44 | + if (*need_zero) { |
45 | + int ret; | ||
45 | + | 46 | + |
46 | /* Options that are not handled are only okay if they are unchanged | 47 | + ret = bdrv_truncate(bs->file, *new_offset + s->block_size, false, |
47 | * compared to the old state. It is expected that some options are only | 48 | + PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, NULL); |
48 | * used for the initial open, but not reopen (e.g. filename) */ | 49 | + if (ret != -ENOTSUP) { |
49 | @@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, | 50 | + *need_zero = false; |
50 | reopen_state->options = qobject_ref(orig_reopen_opts); | 51 | + return ret; |
51 | |||
52 | error: | ||
53 | + if (ret < 0 && drv_prepared) { | ||
54 | + /* drv->bdrv_reopen_prepare() has succeeded, so we need to | ||
55 | + * call drv->bdrv_reopen_abort() before signaling an error | ||
56 | + * (bdrv_reopen_multiple() will not call bdrv_reopen_abort() | ||
57 | + * when the respective bdrv_reopen_prepare() has failed) */ | ||
58 | + if (drv->bdrv_reopen_abort) { | ||
59 | + drv->bdrv_reopen_abort(reopen_state); | ||
60 | + } | 52 | + } |
61 | + } | 53 | + } |
62 | qemu_opts_del(opts); | 54 | + |
63 | qobject_unref(orig_reopen_opts); | 55 | return bdrv_truncate(bs->file, *new_offset + s->block_size, false, |
64 | g_free(discard); | 56 | PREALLOC_MODE_OFF, 0, NULL); |
57 | } | ||
58 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
59 | /* in this case, we need to preserve zero writes for | ||
60 | * data that is not part of this write, so we must pad | ||
61 | * the rest of the buffer to zeroes */ | ||
62 | - | ||
63 | - /* if we are on a posix system with ftruncate() that extends | ||
64 | - * a file, then it is zero-filled for us. On Win32, the raw | ||
65 | - * layer uses SetFilePointer and SetFileEnd, which does not | ||
66 | - * zero fill AFAIK */ | ||
67 | - | ||
68 | - /* Queue another write of zero buffers if the underlying file | ||
69 | - * does not zero-fill on file extension */ | ||
70 | - | ||
71 | - if (bdrv_has_zero_init_truncate(bs->file->bs) == 0) { | ||
72 | - use_zero_buffers = true; | ||
73 | - | ||
74 | + use_zero_buffers = true; | ||
75 | + /* fall through */ | ||
76 | + case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ | ||
77 | + case PAYLOAD_BLOCK_UNMAPPED: | ||
78 | + case PAYLOAD_BLOCK_UNMAPPED_v095: | ||
79 | + case PAYLOAD_BLOCK_UNDEFINED: | ||
80 | + bat_prior_offset = sinfo.file_offset; | ||
81 | + ret = vhdx_allocate_block(bs, s, &sinfo.file_offset, | ||
82 | + &use_zero_buffers); | ||
83 | + if (ret < 0) { | ||
84 | + goto exit; | ||
85 | + } | ||
86 | + /* | ||
87 | + * once we support differencing files, this may also be | ||
88 | + * partially present | ||
89 | + */ | ||
90 | + /* update block state to the newly specified state */ | ||
91 | + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, | ||
92 | + &bat_entry_offset, | ||
93 | + PAYLOAD_BLOCK_FULLY_PRESENT); | ||
94 | + bat_update = true; | ||
95 | + /* | ||
96 | + * Since we just allocated a block, file_offset is the | ||
97 | + * beginning of the payload block. It needs to be the | ||
98 | + * write address, which includes the offset into the | ||
99 | + * block, unless the entire block needs to read as | ||
100 | + * zeroes but truncation was not able to provide them, | ||
101 | + * in which case we need to fill in the rest. | ||
102 | + */ | ||
103 | + if (!use_zero_buffers) { | ||
104 | + sinfo.file_offset += sinfo.block_offset; | ||
105 | + } else { | ||
106 | /* zero fill the front, if any */ | ||
107 | if (sinfo.block_offset) { | ||
108 | iov1.iov_len = sinfo.block_offset; | ||
109 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
110 | } | ||
111 | |||
112 | /* our actual data */ | ||
113 | - qemu_iovec_concat(&hd_qiov, qiov, bytes_done, | ||
114 | + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, | ||
115 | sinfo.bytes_avail); | ||
116 | |||
117 | /* zero fill the back, if any */ | ||
118 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, | ||
119 | sectors_to_write += iov2.iov_len >> BDRV_SECTOR_BITS; | ||
120 | } | ||
121 | } | ||
122 | - /* fall through */ | ||
123 | - case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ | ||
124 | - case PAYLOAD_BLOCK_UNMAPPED: | ||
125 | - case PAYLOAD_BLOCK_UNMAPPED_v095: | ||
126 | - case PAYLOAD_BLOCK_UNDEFINED: | ||
127 | - bat_prior_offset = sinfo.file_offset; | ||
128 | - ret = vhdx_allocate_block(bs, s, &sinfo.file_offset); | ||
129 | - if (ret < 0) { | ||
130 | - goto exit; | ||
131 | - } | ||
132 | - /* once we support differencing files, this may also be | ||
133 | - * partially present */ | ||
134 | - /* update block state to the newly specified state */ | ||
135 | - vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, | ||
136 | - &bat_entry_offset, | ||
137 | - PAYLOAD_BLOCK_FULLY_PRESENT); | ||
138 | - bat_update = true; | ||
139 | - /* since we just allocated a block, file_offset is the | ||
140 | - * beginning of the payload block. It needs to be the | ||
141 | - * write address, which includes the offset into the block */ | ||
142 | - if (!use_zero_buffers) { | ||
143 | - sinfo.file_offset += sinfo.block_offset; | ||
144 | - } | ||
145 | + | ||
146 | /* fall through */ | ||
147 | case PAYLOAD_BLOCK_FULLY_PRESENT: | ||
148 | /* if the file offset address is in the header zone, | ||
65 | -- | 149 | -- |
66 | 2.19.1 | 150 | 2.25.3 |
67 | 151 | ||
68 | 152 | diff view generated by jsdifflib |
1 | From: Max Reitz <mreitz@redhat.com> | 1 | From: Eric Blake <eblake@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | s->locked_shared_perm is the set of bits locked in the file, which is | 3 | Now that there are no clients of bdrv_has_zero_init_truncate, none of |
4 | the inverse of the permissions actually shared. So we need to pass them | 4 | the drivers need to worry about providing it. |
5 | as they are to raw_apply_lock_bytes() instead of inverting them again. | 5 | |
6 | 6 | What's more, this eliminates a source of some confusion: a literal | |
7 | Reported-by: Alberto Garcia <berto@igalia.com> | 7 | reading of the documentation as written in ceaca56f and implemented in |
8 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 8 | commit 1dcaf527 claims that a driver which returns 0 for |
9 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 9 | bdrv_has_zero_init_truncate() must not return 1 for |
10 | bdrv_has_zero_init(); this condition was violated for parallels, qcow, | ||
11 | and sometimes for vdi, although in practice it did not matter since | ||
12 | those drivers also lacked .bdrv_co_truncate. | ||
13 | |||
14 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
15 | Message-Id: <20200428202905.770727-10-eblake@redhat.com> | ||
16 | Acked-by: Richard W.M. Jones <rjones@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 18 | --- |
12 | block/file-posix.c | 2 +- | 19 | include/block/block.h | 1 - |
13 | 1 file changed, 1 insertion(+), 1 deletion(-) | 20 | include/block/block_int.h | 7 ------- |
14 | 21 | block.c | 21 --------------------- | |
22 | block/file-posix.c | 1 - | ||
23 | block/file-win32.c | 1 - | ||
24 | block/nfs.c | 1 - | ||
25 | block/qcow2.c | 1 - | ||
26 | block/qed.c | 1 - | ||
27 | block/raw-format.c | 6 ------ | ||
28 | block/rbd.c | 1 - | ||
29 | block/sheepdog.c | 3 --- | ||
30 | block/ssh.c | 1 - | ||
31 | 12 files changed, 45 deletions(-) | ||
32 | |||
33 | diff --git a/include/block/block.h b/include/block/block.h | ||
34 | index XXXXXXX..XXXXXXX 100644 | ||
35 | --- a/include/block/block.h | ||
36 | +++ b/include/block/block.h | ||
37 | @@ -XXX,XX +XXX,XX @@ int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); | ||
38 | int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); | ||
39 | int bdrv_has_zero_init_1(BlockDriverState *bs); | ||
40 | int bdrv_has_zero_init(BlockDriverState *bs); | ||
41 | -int bdrv_has_zero_init_truncate(BlockDriverState *bs); | ||
42 | bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); | ||
43 | bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); | ||
44 | int bdrv_block_status(BlockDriverState *bs, int64_t offset, | ||
45 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
46 | index XXXXXXX..XXXXXXX 100644 | ||
47 | --- a/include/block/block_int.h | ||
48 | +++ b/include/block/block_int.h | ||
49 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | ||
50 | /* | ||
51 | * Returns 1 if newly created images are guaranteed to contain only | ||
52 | * zeros, 0 otherwise. | ||
53 | - * Must return 0 if .bdrv_has_zero_init_truncate() returns 0. | ||
54 | */ | ||
55 | int (*bdrv_has_zero_init)(BlockDriverState *bs); | ||
56 | |||
57 | - /* | ||
58 | - * Returns 1 if new areas added by growing the image with | ||
59 | - * PREALLOC_MODE_OFF contain only zeros, 0 otherwise. | ||
60 | - */ | ||
61 | - int (*bdrv_has_zero_init_truncate)(BlockDriverState *bs); | ||
62 | - | ||
63 | /* Remove fd handlers, timers, and other event loop callbacks so the event | ||
64 | * loop is no longer in use. Called with no in-flight requests and in | ||
65 | * depth-first traversal order with parents before child nodes. | ||
66 | diff --git a/block.c b/block.c | ||
67 | index XXXXXXX..XXXXXXX 100644 | ||
68 | --- a/block.c | ||
69 | +++ b/block.c | ||
70 | @@ -XXX,XX +XXX,XX @@ int bdrv_has_zero_init(BlockDriverState *bs) | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | -int bdrv_has_zero_init_truncate(BlockDriverState *bs) | ||
75 | -{ | ||
76 | - if (!bs->drv) { | ||
77 | - return 0; | ||
78 | - } | ||
79 | - | ||
80 | - if (bs->backing) { | ||
81 | - /* Depends on the backing image length, but better safe than sorry */ | ||
82 | - return 0; | ||
83 | - } | ||
84 | - if (bs->drv->bdrv_has_zero_init_truncate) { | ||
85 | - return bs->drv->bdrv_has_zero_init_truncate(bs); | ||
86 | - } | ||
87 | - if (bs->file && bs->drv->is_filter) { | ||
88 | - return bdrv_has_zero_init_truncate(bs->file->bs); | ||
89 | - } | ||
90 | - | ||
91 | - /* safe default */ | ||
92 | - return 0; | ||
93 | -} | ||
94 | - | ||
95 | bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) | ||
96 | { | ||
97 | BlockDriverInfo bdi; | ||
15 | diff --git a/block/file-posix.c b/block/file-posix.c | 98 | diff --git a/block/file-posix.c b/block/file-posix.c |
16 | index XXXXXXX..XXXXXXX 100644 | 99 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/block/file-posix.c | 100 | --- a/block/file-posix.c |
18 | +++ b/block/file-posix.c | 101 | +++ b/block/file-posix.c |
19 | @@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state) | 102 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { |
20 | 103 | .bdrv_co_create = raw_co_create, | |
21 | /* Copy locks to the new fd before closing the old one. */ | 104 | .bdrv_co_create_opts = raw_co_create_opts, |
22 | raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, | 105 | .bdrv_has_zero_init = bdrv_has_zero_init_1, |
23 | - ~s->locked_shared_perm, false, &local_err); | 106 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, |
24 | + s->locked_shared_perm, false, &local_err); | 107 | .bdrv_co_block_status = raw_co_block_status, |
25 | if (local_err) { | 108 | .bdrv_co_invalidate_cache = raw_co_invalidate_cache, |
26 | /* shouldn't fail in a sane host, but report it just in case. */ | 109 | .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, |
27 | error_report_err(local_err); | 110 | diff --git a/block/file-win32.c b/block/file-win32.c |
111 | index XXXXXXX..XXXXXXX 100644 | ||
112 | --- a/block/file-win32.c | ||
113 | +++ b/block/file-win32.c | ||
114 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { | ||
115 | .bdrv_close = raw_close, | ||
116 | .bdrv_co_create_opts = raw_co_create_opts, | ||
117 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
118 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
119 | |||
120 | .bdrv_aio_preadv = raw_aio_preadv, | ||
121 | .bdrv_aio_pwritev = raw_aio_pwritev, | ||
122 | diff --git a/block/nfs.c b/block/nfs.c | ||
123 | index XXXXXXX..XXXXXXX 100644 | ||
124 | --- a/block/nfs.c | ||
125 | +++ b/block/nfs.c | ||
126 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nfs = { | ||
127 | .create_opts = &nfs_create_opts, | ||
128 | |||
129 | .bdrv_has_zero_init = nfs_has_zero_init, | ||
130 | - .bdrv_has_zero_init_truncate = nfs_has_zero_init, | ||
131 | .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, | ||
132 | .bdrv_co_truncate = nfs_file_co_truncate, | ||
133 | |||
134 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
135 | index XXXXXXX..XXXXXXX 100644 | ||
136 | --- a/block/qcow2.c | ||
137 | +++ b/block/qcow2.c | ||
138 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = { | ||
139 | .bdrv_co_create_opts = qcow2_co_create_opts, | ||
140 | .bdrv_co_create = qcow2_co_create, | ||
141 | .bdrv_has_zero_init = qcow2_has_zero_init, | ||
142 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
143 | .bdrv_co_block_status = qcow2_co_block_status, | ||
144 | |||
145 | .bdrv_co_preadv_part = qcow2_co_preadv_part, | ||
146 | diff --git a/block/qed.c b/block/qed.c | ||
147 | index XXXXXXX..XXXXXXX 100644 | ||
148 | --- a/block/qed.c | ||
149 | +++ b/block/qed.c | ||
150 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = { | ||
151 | .bdrv_co_create = bdrv_qed_co_create, | ||
152 | .bdrv_co_create_opts = bdrv_qed_co_create_opts, | ||
153 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
154 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
155 | .bdrv_co_block_status = bdrv_qed_co_block_status, | ||
156 | .bdrv_co_readv = bdrv_qed_co_readv, | ||
157 | .bdrv_co_writev = bdrv_qed_co_writev, | ||
158 | diff --git a/block/raw-format.c b/block/raw-format.c | ||
159 | index XXXXXXX..XXXXXXX 100644 | ||
160 | --- a/block/raw-format.c | ||
161 | +++ b/block/raw-format.c | ||
162 | @@ -XXX,XX +XXX,XX @@ static int raw_has_zero_init(BlockDriverState *bs) | ||
163 | return bdrv_has_zero_init(bs->file->bs); | ||
164 | } | ||
165 | |||
166 | -static int raw_has_zero_init_truncate(BlockDriverState *bs) | ||
167 | -{ | ||
168 | - return bdrv_has_zero_init_truncate(bs->file->bs); | ||
169 | -} | ||
170 | - | ||
171 | static int coroutine_fn raw_co_create_opts(BlockDriver *drv, | ||
172 | const char *filename, | ||
173 | QemuOpts *opts, | ||
174 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = { | ||
175 | .bdrv_co_ioctl = &raw_co_ioctl, | ||
176 | .create_opts = &raw_create_opts, | ||
177 | .bdrv_has_zero_init = &raw_has_zero_init, | ||
178 | - .bdrv_has_zero_init_truncate = &raw_has_zero_init_truncate, | ||
179 | .strong_runtime_opts = raw_strong_runtime_opts, | ||
180 | .mutable_opts = mutable_opts, | ||
181 | }; | ||
182 | diff --git a/block/rbd.c b/block/rbd.c | ||
183 | index XXXXXXX..XXXXXXX 100644 | ||
184 | --- a/block/rbd.c | ||
185 | +++ b/block/rbd.c | ||
186 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = { | ||
187 | .bdrv_co_create = qemu_rbd_co_create, | ||
188 | .bdrv_co_create_opts = qemu_rbd_co_create_opts, | ||
189 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
190 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
191 | .bdrv_get_info = qemu_rbd_getinfo, | ||
192 | .create_opts = &qemu_rbd_create_opts, | ||
193 | .bdrv_getlength = qemu_rbd_getlength, | ||
194 | diff --git a/block/sheepdog.c b/block/sheepdog.c | ||
195 | index XXXXXXX..XXXXXXX 100644 | ||
196 | --- a/block/sheepdog.c | ||
197 | +++ b/block/sheepdog.c | ||
198 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog = { | ||
199 | .bdrv_co_create = sd_co_create, | ||
200 | .bdrv_co_create_opts = sd_co_create_opts, | ||
201 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
202 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
203 | .bdrv_getlength = sd_getlength, | ||
204 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||
205 | .bdrv_co_truncate = sd_co_truncate, | ||
206 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_tcp = { | ||
207 | .bdrv_co_create = sd_co_create, | ||
208 | .bdrv_co_create_opts = sd_co_create_opts, | ||
209 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
210 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
211 | .bdrv_getlength = sd_getlength, | ||
212 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||
213 | .bdrv_co_truncate = sd_co_truncate, | ||
214 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_sheepdog_unix = { | ||
215 | .bdrv_co_create = sd_co_create, | ||
216 | .bdrv_co_create_opts = sd_co_create_opts, | ||
217 | .bdrv_has_zero_init = bdrv_has_zero_init_1, | ||
218 | - .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, | ||
219 | .bdrv_getlength = sd_getlength, | ||
220 | .bdrv_get_allocated_file_size = sd_get_allocated_file_size, | ||
221 | .bdrv_co_truncate = sd_co_truncate, | ||
222 | diff --git a/block/ssh.c b/block/ssh.c | ||
223 | index XXXXXXX..XXXXXXX 100644 | ||
224 | --- a/block/ssh.c | ||
225 | +++ b/block/ssh.c | ||
226 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ssh = { | ||
227 | .bdrv_co_create_opts = ssh_co_create_opts, | ||
228 | .bdrv_close = ssh_close, | ||
229 | .bdrv_has_zero_init = ssh_has_zero_init, | ||
230 | - .bdrv_has_zero_init_truncate = ssh_has_zero_init, | ||
231 | .bdrv_co_readv = ssh_co_readv, | ||
232 | .bdrv_co_writev = ssh_co_writev, | ||
233 | .bdrv_getlength = ssh_getlength, | ||
28 | -- | 234 | -- |
29 | 2.19.1 | 235 | 2.25.3 |
30 | 236 | ||
31 | 237 | diff view generated by jsdifflib |