From: Denis V. Lunev <[email protected]> 040 never commits runs long enough to span more than one block-status query, so the answer that commit_iteration() is about to start caching goes untested.
Add two cases on a base <- mid <- active chain, committing mid so the job takes the regular commit path rather than active commit, and compare base against a snapshot of mid taken before the commit. One case mixes multi-megabyte data, zero and hole runs, the other fragments them down to single clusters with every transition off the 512K boundary. Signed-off-by: Denis V. Lunev <[email protected]> CC: Vladimir Sementsov-Ogievskiy <[email protected]> CC: John Snow <[email protected]> CC: Andrey Drobyshev <[email protected]> --- tests/qemu-iotests/040 | 102 ++++++++++++++++++++++++++++++++++++- tests/qemu-iotests/040.out | 4 +- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 5c18e413ec..e01c9385ba 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -25,13 +25,14 @@ import time import os import iotests -from iotests import qemu_img, qemu_io +from iotests import qemu_img, qemu_img_create, qemu_io, compare_images import struct import errno backing_img = os.path.join(iotests.test_dir, 'backing.img') mid_img = os.path.join(iotests.test_dir, 'mid.img') test_img = os.path.join(iotests.test_dir, 'test.img') +reference_img = os.path.join(iotests.test_dir, 'reference.img') class ImageCommitTestCase(iotests.QMPTestCase): '''Abstract base class for image commit test cases''' @@ -951,6 +952,105 @@ class TestCommitWithOverriddenBacking(iotests.QMPTestCase): self.vm.qmp('block-job-complete', device='commit') self.vm.event_wait('BLOCK_JOB_COMPLETED') +class TestCommitLargeRuns(iotests.QMPTestCase): + """Commit runs long enough to cross commit_iteration()'s cached span.""" + + MB = 1024 * 1024 + CLUSTER = 64 * 1024 + + # Runs several COMMIT_BUFFER_SIZE (512K) chunks long, of every kind. + SIZE = 32 * MB + LAYOUT = [ + (0, 4 * MB, 'data'), + (4 * MB, 6 * MB, 'hole'), + (10 * MB, 4 * MB, 'data'), + (14 * MB, 6 * MB, 'zero'), + (20 * MB, 4 * MB, 'data'), + (24 * MB, 8 * MB, 'hole'), + ] + + # The same, with every transition off the 512K boundary. + SIZE_FRAGMENTED = 384 * CLUSTER # 24M + LAYOUT_FRAGMENTED = [ + (0, 45 * CLUSTER, 'data'), + (45 * CLUSTER, 55 * CLUSTER, 'hole'), + (100 * CLUSTER, CLUSTER, 'data'), + (101 * CLUSTER, 49 * CLUSTER, 'zero'), + (150 * CLUSTER, 80 * CLUSTER, 'data'), + (230 * CLUSTER, CLUSTER, 'hole'), + (231 * CLUSTER, 69 * CLUSTER, 'data'), + (300 * CLUSTER, 83 * CLUSTER, 'zero'), + (383 * CLUSTER, CLUSTER, 'hole'), + ] + + def setUp(self): + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + for img in (backing_img, mid_img, test_img, reference_img): + if os.path.exists(img): + os.remove(img) + + def build_images(self, layout, size): + # A pattern of its own in base, so a misplaced cluster shows up. + qemu_img_create('-f', iotests.imgfmt, backing_img, str(size)) + qemu_io('-c', f'write -P 0x11 0 {size}', backing_img) + + qemu_img_create('-f', iotests.imgfmt, '-b', backing_img, '-F', + iotests.imgfmt, mid_img, str(size)) + for offset, length, kind in layout: + if kind == 'data': + qemu_io('-c', f'write -P 0x22 {offset} {length}', mid_img) + elif kind == 'zero': + qemu_io('-c', f'write -z {offset} {length}', mid_img) + + # What base must equal once mid is committed into it. + qemu_img('convert', '-f', iotests.imgfmt, '-O', iotests.imgfmt, + mid_img, reference_img) + + # An empty layer above mid, so top_node=mid is not the active one. + qemu_img_create('-f', iotests.imgfmt, '-b', mid_img, '-F', + iotests.imgfmt, test_img, str(size)) + + self.vm.cmd('blockdev-add', { + 'node-name': 'base', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', 'filename': backing_img}, + }) + self.vm.cmd('blockdev-add', { + 'node-name': 'mid', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', 'filename': mid_img}, + 'backing': 'base', + }) + self.vm.cmd('blockdev-add', { + 'node-name': 'active', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', 'filename': test_img}, + 'backing': 'mid', + }) + + def commit_and_verify(self): + self.vm.cmd('block-commit', job_id='commit0', device='active', + top_node='mid', base_node='base') + self.wait_until_completed(drive='commit0') + + self.vm.cmd('blockdev-del', node_name='active') + self.vm.cmd('blockdev-del', node_name='mid') + self.vm.cmd('blockdev-del', node_name='base') + self.assertTrue(compare_images(reference_img, backing_img)) + + def test_commit_large_runs(self): + self.build_images(self.LAYOUT, self.SIZE) + self.commit_and_verify() + + def test_commit_fragmented_runs(self): + self.build_images(self.LAYOUT_FRAGMENTED, self.SIZE_FRAGMENTED) + self.commit_and_verify() + + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out index 1bb1dc5f0e..d2e2a2d98f 100644 --- a/tests/qemu-iotests/040.out +++ b/tests/qemu-iotests/040.out @@ -1,5 +1,5 @@ -................................................................. +................................................................... ---------------------------------------------------------------------- -Ran 65 tests +Ran 67 tests OK -- 2.53.0
