xref: /openbmc/qemu/tests/qemu-iotests/151 (revision 7b5929c73485b31ebc87c4c20328a6cb40519b71)
1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3
29dd003a9SVladimir Sementsov-Ogievskiy# group: rw
3e38da020SMax Reitz#
4e38da020SMax Reitz# Tests for active mirroring
5e38da020SMax Reitz#
6e38da020SMax Reitz# Copyright (C) 2018 Red Hat, Inc.
7e38da020SMax Reitz#
8e38da020SMax Reitz# This program is free software; you can redistribute it and/or modify
9e38da020SMax Reitz# it under the terms of the GNU General Public License as published by
10e38da020SMax Reitz# the Free Software Foundation; either version 2 of the License, or
11e38da020SMax Reitz# (at your option) any later version.
12e38da020SMax Reitz#
13e38da020SMax Reitz# This program is distributed in the hope that it will be useful,
14e38da020SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of
15e38da020SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16e38da020SMax Reitz# GNU General Public License for more details.
17e38da020SMax Reitz#
18e38da020SMax Reitz# You should have received a copy of the GNU General Public License
19e38da020SMax Reitz# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20e38da020SMax Reitz#
21e38da020SMax Reitz
2238591290SHanna Reitzimport math
23e38da020SMax Reitzimport os
2438591290SHanna Reitzimport subprocess
25*7b5929c7SHanna Reitzimport time
26*7b5929c7SHanna Reitzfrom typing import List, Optional
27e38da020SMax Reitzimport iotests
28e38da020SMax Reitzfrom iotests import qemu_img
29e38da020SMax Reitz
30e38da020SMax Reitzsource_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt)
31e38da020SMax Reitztarget_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
32e38da020SMax Reitz
33e38da020SMax Reitzclass TestActiveMirror(iotests.QMPTestCase):
34e38da020SMax Reitz    image_len = 128 * 1024 * 1024 # MB
35e38da020SMax Reitz    potential_writes_in_flight = True
36e38da020SMax Reitz
37e38da020SMax Reitz    def setUp(self):
38e38da020SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
39e38da020SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
40e38da020SMax Reitz
41e38da020SMax Reitz        blk_source = {'id': 'source',
42e38da020SMax Reitz                      'if': 'none',
43e38da020SMax Reitz                      'node-name': 'source-node',
44e38da020SMax Reitz                      'driver': iotests.imgfmt,
45e0f69d83SVladimir Sementsov-Ogievskiy                      'file': {'driver': 'blkdebug',
46e0f69d83SVladimir Sementsov-Ogievskiy                               'image': {'driver': 'file',
47e0f69d83SVladimir Sementsov-Ogievskiy                                         'filename': source_img}}}
48e38da020SMax Reitz
49e38da020SMax Reitz        blk_target = {'node-name': 'target-node',
50e38da020SMax Reitz                      'driver': iotests.imgfmt,
51e38da020SMax Reitz                      'file': {'driver': 'file',
52e38da020SMax Reitz                               'filename': target_img}}
53e38da020SMax Reitz
54e38da020SMax Reitz        self.vm = iotests.VM()
55e38da020SMax Reitz        self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source))
56e38da020SMax Reitz        self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target))
5738591290SHanna Reitz        self.vm.add_device('virtio-blk,id=vblk,drive=source')
58e38da020SMax Reitz        self.vm.launch()
59e38da020SMax Reitz
60e38da020SMax Reitz    def tearDown(self):
61e38da020SMax Reitz        self.vm.shutdown()
62e38da020SMax Reitz
63e38da020SMax Reitz        if not self.potential_writes_in_flight:
64e38da020SMax Reitz            self.assertTrue(iotests.compare_images(source_img, target_img),
65e38da020SMax Reitz                            'mirror target does not match source')
66e38da020SMax Reitz
67e38da020SMax Reitz        os.remove(source_img)
68e38da020SMax Reitz        os.remove(target_img)
69e38da020SMax Reitz
70e38da020SMax Reitz    def doActiveIO(self, sync_source_and_target):
71e38da020SMax Reitz        # Fill the source image
72e38da020SMax Reitz        self.vm.hmp_qemu_io('source',
73e38da020SMax Reitz                            'write -P 1 0 %i' % self.image_len);
74e38da020SMax Reitz
75e38da020SMax Reitz        # Start some background requests
769a3a9a63SMax Reitz        for offset in range(1 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024):
77e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset)
789a3a9a63SMax Reitz        for offset in range(2 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024):
79e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
80e38da020SMax Reitz
81e38da020SMax Reitz        # Start the block job
82e38da020SMax Reitz        result = self.vm.qmp('blockdev-mirror',
83e38da020SMax Reitz                             job_id='mirror',
84e38da020SMax Reitz                             filter_node_name='mirror-node',
85e38da020SMax Reitz                             device='source-node',
86e38da020SMax Reitz                             target='target-node',
87e38da020SMax Reitz                             sync='full',
88e38da020SMax Reitz                             copy_mode='write-blocking')
89e38da020SMax Reitz        self.assert_qmp(result, 'return', {})
90e38da020SMax Reitz
91e38da020SMax Reitz        # Start some more requests
929a3a9a63SMax Reitz        for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024):
93e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
949a3a9a63SMax Reitz        for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024):
95e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
96e38da020SMax Reitz
97e38da020SMax Reitz        # Wait for the READY event
98e38da020SMax Reitz        self.wait_ready(drive='mirror')
99e38da020SMax Reitz
100e38da020SMax Reitz        # Now start some final requests; all of these (which land on
101e38da020SMax Reitz        # the source) should be settled using the active mechanism.
102e38da020SMax Reitz        # The mirror code itself asserts that the source BDS's dirty
103e38da020SMax Reitz        # bitmap will stay clean between READY and COMPLETED.
1049a3a9a63SMax Reitz        for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024):
105e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
1069a3a9a63SMax Reitz        for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024):
107e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
108e38da020SMax Reitz
109e38da020SMax Reitz        if sync_source_and_target:
110e38da020SMax Reitz            # If source and target should be in sync after the mirror,
111e38da020SMax Reitz            # we have to flush before completion
112e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_flush')
113e38da020SMax Reitz            self.potential_writes_in_flight = False
114e38da020SMax Reitz
115e38da020SMax Reitz        self.complete_and_wait(drive='mirror', wait_ready=False)
116e38da020SMax Reitz
117e38da020SMax Reitz    def testActiveIO(self):
118e38da020SMax Reitz        self.doActiveIO(False)
119e38da020SMax Reitz
120e38da020SMax Reitz    def testActiveIOFlushed(self):
121e38da020SMax Reitz        self.doActiveIO(True)
122e38da020SMax Reitz
12319ba4651SMax Reitz    def testUnalignedActiveIO(self):
12419ba4651SMax Reitz        # Fill the source image
12519ba4651SMax Reitz        result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M')
12619ba4651SMax Reitz
12719ba4651SMax Reitz        # Start the block job (very slowly)
12819ba4651SMax Reitz        result = self.vm.qmp('blockdev-mirror',
12919ba4651SMax Reitz                             job_id='mirror',
13019ba4651SMax Reitz                             filter_node_name='mirror-node',
13119ba4651SMax Reitz                             device='source-node',
13219ba4651SMax Reitz                             target='target-node',
13319ba4651SMax Reitz                             sync='full',
13419ba4651SMax Reitz                             copy_mode='write-blocking',
13519ba4651SMax Reitz                             buf_size=(1048576 // 4),
13619ba4651SMax Reitz                             speed=1)
13719ba4651SMax Reitz        self.assert_qmp(result, 'return', {})
13819ba4651SMax Reitz
13919ba4651SMax Reitz        # Start an unaligned request to a dirty area
14019ba4651SMax Reitz        result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42))
14119ba4651SMax Reitz
14219ba4651SMax Reitz        # Let the job finish
14319ba4651SMax Reitz        result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0)
14419ba4651SMax Reitz        self.assert_qmp(result, 'return', {})
14519ba4651SMax Reitz        self.complete_and_wait(drive='mirror')
14619ba4651SMax Reitz
14719ba4651SMax Reitz        self.potential_writes_in_flight = False
148e38da020SMax Reitz
149e0f69d83SVladimir Sementsov-Ogievskiy    def testIntersectingActiveIO(self):
150e0f69d83SVladimir Sementsov-Ogievskiy        # Fill the source image
151e0f69d83SVladimir Sementsov-Ogievskiy        result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M')
152e0f69d83SVladimir Sementsov-Ogievskiy
153e0f69d83SVladimir Sementsov-Ogievskiy        # Start the block job (very slowly)
154e0f69d83SVladimir Sementsov-Ogievskiy        result = self.vm.qmp('blockdev-mirror',
155e0f69d83SVladimir Sementsov-Ogievskiy                             job_id='mirror',
156e0f69d83SVladimir Sementsov-Ogievskiy                             filter_node_name='mirror-node',
157e0f69d83SVladimir Sementsov-Ogievskiy                             device='source-node',
158e0f69d83SVladimir Sementsov-Ogievskiy                             target='target-node',
159e0f69d83SVladimir Sementsov-Ogievskiy                             sync='full',
160e0f69d83SVladimir Sementsov-Ogievskiy                             copy_mode='write-blocking',
161e0f69d83SVladimir Sementsov-Ogievskiy                             speed=1)
162e0f69d83SVladimir Sementsov-Ogievskiy
163e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'break write_aio A')
164e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 1M')  # 1
165e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'wait_break A')
166e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 2M')  # 2
167e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 2M')  # 3
168e0f69d83SVladimir Sementsov-Ogievskiy
169e0f69d83SVladimir Sementsov-Ogievskiy        # Now 2 and 3 are in mirror_wait_on_conflicts, waiting for 1
170e0f69d83SVladimir Sementsov-Ogievskiy
171e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'break write_aio B')
172e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 1M 2M')  # 4
173e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'wait_break B')
174e0f69d83SVladimir Sementsov-Ogievskiy
175e0f69d83SVladimir Sementsov-Ogievskiy        # 4 doesn't wait for 2 and 3, because they didn't yet set
176e0f69d83SVladimir Sementsov-Ogievskiy        # in_flight_bitmap. So, nothing prevents 4 to go except for our
177e0f69d83SVladimir Sementsov-Ogievskiy        # break-point B.
178e0f69d83SVladimir Sementsov-Ogievskiy
179e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'resume A')
180e0f69d83SVladimir Sementsov-Ogievskiy
181e0f69d83SVladimir Sementsov-Ogievskiy        # Now we resumed 1, so 2 and 3 goes to the next iteration of while loop
182e0f69d83SVladimir Sementsov-Ogievskiy        # in mirror_wait_on_conflicts(). They don't exit, as bitmap is dirty
183d44dae1aSVladimir Sementsov-Ogievskiy        # due to request 4.
184d44dae1aSVladimir Sementsov-Ogievskiy        # In the past at that point 2 and 3 would wait for each other producing
185d44dae1aSVladimir Sementsov-Ogievskiy        # a dead-lock. Now this is fixed and they will wait for request 4.
186e0f69d83SVladimir Sementsov-Ogievskiy
187e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'resume B')
188e0f69d83SVladimir Sementsov-Ogievskiy
189d44dae1aSVladimir Sementsov-Ogievskiy        # After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap,
190d44dae1aSVladimir Sementsov-Ogievskiy        # so the other will wait for it.
191e0f69d83SVladimir Sementsov-Ogievskiy
192e0f69d83SVladimir Sementsov-Ogievskiy        result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0)
193e0f69d83SVladimir Sementsov-Ogievskiy        self.assert_qmp(result, 'return', {})
194e0f69d83SVladimir Sementsov-Ogievskiy        self.complete_and_wait(drive='mirror')
195e0f69d83SVladimir Sementsov-Ogievskiy
196e0f69d83SVladimir Sementsov-Ogievskiy        self.potential_writes_in_flight = False
197e0f69d83SVladimir Sementsov-Ogievskiy
198e38da020SMax Reitz
199*7b5929c7SHanna Reitzclass TestThrottledWithNbdExportBase(iotests.QMPTestCase):
20038591290SHanna Reitz    image_len = 128 * 1024 * 1024  # MB
201*7b5929c7SHanna Reitz    iops: Optional[int] = None
20238591290SHanna Reitz    background_processes: List['subprocess.Popen[str]'] = []
20338591290SHanna Reitz
20438591290SHanna Reitz    def setUp(self):
205*7b5929c7SHanna Reitz        # Must be set by subclasses
206*7b5929c7SHanna Reitz        self.assertIsNotNone(self.iops)
207*7b5929c7SHanna Reitz
20838591290SHanna Reitz        qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
20938591290SHanna Reitz        qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
21038591290SHanna Reitz
21138591290SHanna Reitz        self.vm = iotests.VM()
21238591290SHanna Reitz        self.vm.launch()
21338591290SHanna Reitz
21438591290SHanna Reitz        result = self.vm.qmp('object-add', **{
21538591290SHanna Reitz            'qom-type': 'throttle-group',
21638591290SHanna Reitz            'id': 'thrgr',
21738591290SHanna Reitz            'limits': {
21838591290SHanna Reitz                'iops-total': self.iops,
21938591290SHanna Reitz                'iops-total-max': self.iops
22038591290SHanna Reitz            }
22138591290SHanna Reitz        })
22238591290SHanna Reitz        self.assert_qmp(result, 'return', {})
22338591290SHanna Reitz
22438591290SHanna Reitz        result = self.vm.qmp('blockdev-add', **{
22538591290SHanna Reitz            'node-name': 'source-node',
22638591290SHanna Reitz            'driver': 'throttle',
22738591290SHanna Reitz            'throttle-group': 'thrgr',
22838591290SHanna Reitz            'file': {
22938591290SHanna Reitz                'driver': iotests.imgfmt,
23038591290SHanna Reitz                'file': {
23138591290SHanna Reitz                    'driver': 'file',
23238591290SHanna Reitz                    'filename': source_img
23338591290SHanna Reitz                }
23438591290SHanna Reitz            }
23538591290SHanna Reitz        })
23638591290SHanna Reitz        self.assert_qmp(result, 'return', {})
23738591290SHanna Reitz
23838591290SHanna Reitz        result = self.vm.qmp('blockdev-add', **{
23938591290SHanna Reitz            'node-name': 'target-node',
24038591290SHanna Reitz            'driver': iotests.imgfmt,
24138591290SHanna Reitz            'file': {
24238591290SHanna Reitz                'driver': 'file',
24338591290SHanna Reitz                'filename': target_img
24438591290SHanna Reitz            }
24538591290SHanna Reitz        })
24638591290SHanna Reitz        self.assert_qmp(result, 'return', {})
24738591290SHanna Reitz
24838591290SHanna Reitz        self.nbd_sock = iotests.file_path('nbd.sock',
24938591290SHanna Reitz                                          base_dir=iotests.sock_dir)
25038591290SHanna Reitz        self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}'
25138591290SHanna Reitz
25238591290SHanna Reitz        result = self.vm.qmp('nbd-server-start', addr={
25338591290SHanna Reitz            'type': 'unix',
25438591290SHanna Reitz            'data': {
25538591290SHanna Reitz                'path': self.nbd_sock
25638591290SHanna Reitz            }
25738591290SHanna Reitz        })
25838591290SHanna Reitz        self.assert_qmp(result, 'return', {})
25938591290SHanna Reitz
26038591290SHanna Reitz        result = self.vm.qmp('block-export-add', id='exp0', type='nbd',
26138591290SHanna Reitz                             node_name='source-node', writable=True)
26238591290SHanna Reitz        self.assert_qmp(result, 'return', {})
26338591290SHanna Reitz
26438591290SHanna Reitz    def tearDown(self):
26538591290SHanna Reitz        # Wait for background requests to settle
26638591290SHanna Reitz        try:
26738591290SHanna Reitz            while True:
26838591290SHanna Reitz                p = self.background_processes.pop()
26938591290SHanna Reitz                while True:
27038591290SHanna Reitz                    try:
27138591290SHanna Reitz                        p.wait(timeout=0.0)
27238591290SHanna Reitz                        break
27338591290SHanna Reitz                    except subprocess.TimeoutExpired:
27438591290SHanna Reitz                        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
27538591290SHanna Reitz        except IndexError:
27638591290SHanna Reitz            pass
27738591290SHanna Reitz
27838591290SHanna Reitz        # Cancel ongoing block jobs
27938591290SHanna Reitz        for job in self.vm.qmp('query-jobs')['return']:
28038591290SHanna Reitz            self.vm.qmp('block-job-cancel', device=job['id'], force=True)
28138591290SHanna Reitz
28238591290SHanna Reitz        while True:
28338591290SHanna Reitz            self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
28438591290SHanna Reitz            if len(self.vm.qmp('query-jobs')['return']) == 0:
28538591290SHanna Reitz                break
28638591290SHanna Reitz
28738591290SHanna Reitz        self.vm.shutdown()
28838591290SHanna Reitz        os.remove(source_img)
28938591290SHanna Reitz        os.remove(target_img)
29038591290SHanna Reitz
291*7b5929c7SHanna Reitz
292*7b5929c7SHanna Reitzclass TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase):
293*7b5929c7SHanna Reitz    iops = 16
294*7b5929c7SHanna Reitz
29538591290SHanna Reitz    def testUnderLoad(self):
29638591290SHanna Reitz        '''
29738591290SHanna Reitz        Throttle the source node, then issue a whole bunch of external requests
29838591290SHanna Reitz        while the mirror job (in write-blocking mode) is running.  We want to
29938591290SHanna Reitz        see background requests being issued even while the source is under
30038591290SHanna Reitz        full load by active writes, so that progress can be made towards READY.
30138591290SHanna Reitz        '''
30238591290SHanna Reitz
30338591290SHanna Reitz        # Fill the first half of the source image; do not fill the second half,
30438591290SHanna Reitz        # that is where we will have active requests occur.  This ensures that
30538591290SHanna Reitz        # active mirroring itself will not directly contribute to the job's
30638591290SHanna Reitz        # progress (because when the job was started, those areas were not
30738591290SHanna Reitz        # intended to be copied, so active mirroring will only lead to not
30838591290SHanna Reitz        # losing progress, but also not making any).
30938591290SHanna Reitz        self.vm.hmp_qemu_io('source-node',
31038591290SHanna Reitz                            f'aio_write -P 1 0 {self.image_len // 2}')
31138591290SHanna Reitz        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
31238591290SHanna Reitz
31338591290SHanna Reitz        # Launch the mirror job
31438591290SHanna Reitz        mirror_buf_size = 65536
31538591290SHanna Reitz        result = self.vm.qmp('blockdev-mirror',
31638591290SHanna Reitz                             job_id='mirror',
31738591290SHanna Reitz                             filter_node_name='mirror-node',
31838591290SHanna Reitz                             device='source-node',
31938591290SHanna Reitz                             target='target-node',
32038591290SHanna Reitz                             sync='full',
32138591290SHanna Reitz                             copy_mode='write-blocking',
32238591290SHanna Reitz                             buf_size=mirror_buf_size)
32338591290SHanna Reitz        self.assert_qmp(result, 'return', {})
32438591290SHanna Reitz
32538591290SHanna Reitz        # We create the external requests via qemu-io processes on the NBD
32638591290SHanna Reitz        # server.  Have their offset start in the middle of the image so they
32738591290SHanna Reitz        # do not overlap with the background requests (which start from the
32838591290SHanna Reitz        # beginning).
32938591290SHanna Reitz        active_request_offset = self.image_len // 2
33038591290SHanna Reitz        active_request_len = 4096
33138591290SHanna Reitz
33238591290SHanna Reitz        # Create enough requests to saturate the node for 5 seconds
33338591290SHanna Reitz        for _ in range(0, 5 * self.iops):
33438591290SHanna Reitz            req = f'write -P 42 {active_request_offset} {active_request_len}'
33538591290SHanna Reitz            active_request_offset += active_request_len
33638591290SHanna Reitz            p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
33738591290SHanna Reitz            self.background_processes += [p]
33838591290SHanna Reitz
33938591290SHanna Reitz        # Now advance the clock one I/O operation at a time by the 4 seconds
34038591290SHanna Reitz        # (i.e. one less than 5).  We expect the mirror job to issue background
34138591290SHanna Reitz        # operations here, even though active requests are still in flight.
34238591290SHanna Reitz        # The active requests will take precedence, however, because they have
34338591290SHanna Reitz        # been issued earlier than mirror's background requests.
34438591290SHanna Reitz        # Once the active requests we have started above are done (i.e. after 5
34538591290SHanna Reitz        # virtual seconds), we expect those background requests to be worked
34638591290SHanna Reitz        # on.  We only advance 4 seconds here to avoid race conditions.
34738591290SHanna Reitz        for _ in range(0, 4 * self.iops):
34838591290SHanna Reitz            step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
34938591290SHanna Reitz            self.vm.qtest(f'clock_step {step}')
35038591290SHanna Reitz
35138591290SHanna Reitz        # Note how much remains to be done until the mirror job is finished
35238591290SHanna Reitz        job_status = self.vm.qmp('query-jobs')['return'][0]
35338591290SHanna Reitz        start_remaining = job_status['total-progress'] - \
35438591290SHanna Reitz            job_status['current-progress']
35538591290SHanna Reitz
35638591290SHanna Reitz        # Create a whole bunch of more active requests
35738591290SHanna Reitz        for _ in range(0, 10 * self.iops):
35838591290SHanna Reitz            req = f'write -P 42 {active_request_offset} {active_request_len}'
35938591290SHanna Reitz            active_request_offset += active_request_len
36038591290SHanna Reitz            p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
36138591290SHanna Reitz            self.background_processes += [p]
36238591290SHanna Reitz
36338591290SHanna Reitz        # Let the clock advance more.  After 1 second, as noted above, we
36438591290SHanna Reitz        # expect the background requests to be worked on.  Give them a couple
36538591290SHanna Reitz        # of seconds (specifically 4) to see their impact.
36638591290SHanna Reitz        for _ in range(0, 5 * self.iops):
36738591290SHanna Reitz            step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
36838591290SHanna Reitz            self.vm.qtest(f'clock_step {step}')
36938591290SHanna Reitz
37038591290SHanna Reitz        # Note how much remains to be done now.  We expect this number to be
37138591290SHanna Reitz        # reduced thanks to those background requests.
37238591290SHanna Reitz        job_status = self.vm.qmp('query-jobs')['return'][0]
37338591290SHanna Reitz        end_remaining = job_status['total-progress'] - \
37438591290SHanna Reitz            job_status['current-progress']
37538591290SHanna Reitz
37638591290SHanna Reitz        # See that indeed progress was being made on the job, even while the
37738591290SHanna Reitz        # node was saturated with active requests
37838591290SHanna Reitz        self.assertGreater(start_remaining - end_remaining, 0)
37938591290SHanna Reitz
38038591290SHanna Reitz
381*7b5929c7SHanna Reitzclass TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase):
382*7b5929c7SHanna Reitz    iops = 1024
383*7b5929c7SHanna Reitz
384*7b5929c7SHanna Reitz    def testActiveOnCreation(self):
385*7b5929c7SHanna Reitz        '''
386*7b5929c7SHanna Reitz        Issue requests on the mirror source node right as the mirror is
387*7b5929c7SHanna Reitz        instated.  It's possible that requests occur before the actual job is
388*7b5929c7SHanna Reitz        created, but after the node has been put into the graph.  Write
389*7b5929c7SHanna Reitz        requests across the node must in that case be forwarded to the source
390*7b5929c7SHanna Reitz        node without attempting to mirror them (there is no job object yet, so
391*7b5929c7SHanna Reitz        attempting to access it would cause a segfault).
392*7b5929c7SHanna Reitz        We do this with a lightly throttled node (i.e. quite high IOPS limit).
393*7b5929c7SHanna Reitz        Using throttling seems to increase reproductivity, but if the limit is
394*7b5929c7SHanna Reitz        too low, all requests allowed per second will be submitted before
395*7b5929c7SHanna Reitz        mirror_start_job() gets to the problematic point.
396*7b5929c7SHanna Reitz        '''
397*7b5929c7SHanna Reitz
398*7b5929c7SHanna Reitz        # Let qemu-img bench create write requests (enough for two seconds on
399*7b5929c7SHanna Reitz        # the virtual clock)
400*7b5929c7SHanna Reitz        bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd',
401*7b5929c7SHanna Reitz                      '-c', str(self.iops * 2), self.nbd_url]
402*7b5929c7SHanna Reitz        p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args)
403*7b5929c7SHanna Reitz        self.background_processes += [p]
404*7b5929c7SHanna Reitz
405*7b5929c7SHanna Reitz        # Give qemu-img bench time to start up and issue requests
406*7b5929c7SHanna Reitz        time.sleep(1.0)
407*7b5929c7SHanna Reitz        # Flush the request queue, so new requests can come in right as we
408*7b5929c7SHanna Reitz        # start blockdev-mirror
409*7b5929c7SHanna Reitz        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
410*7b5929c7SHanna Reitz
411*7b5929c7SHanna Reitz        result = self.vm.qmp('blockdev-mirror',
412*7b5929c7SHanna Reitz                             job_id='mirror',
413*7b5929c7SHanna Reitz                             device='source-node',
414*7b5929c7SHanna Reitz                             target='target-node',
415*7b5929c7SHanna Reitz                             sync='full',
416*7b5929c7SHanna Reitz                             copy_mode='write-blocking')
417*7b5929c7SHanna Reitz        self.assert_qmp(result, 'return', {})
418*7b5929c7SHanna Reitz
419*7b5929c7SHanna Reitz
420e38da020SMax Reitzif __name__ == '__main__':
421103cbc77SMax Reitz    iotests.main(supported_fmts=['qcow2', 'raw'],
422103cbc77SMax Reitz                 supported_protocols=['file'])
423