xref: /openbmc/qemu/tests/qemu-iotests/151 (revision 800485762e6564e04e2ab315132d477069562d91)
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
257b5929c7SHanna Reitzimport time
267b5929c7SHanna 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
82*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('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
90e38da020SMax Reitz        # Start some more requests
919a3a9a63SMax Reitz        for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024):
92e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
939a3a9a63SMax Reitz        for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024):
94e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
95e38da020SMax Reitz
96e38da020SMax Reitz        # Wait for the READY event
97e38da020SMax Reitz        self.wait_ready(drive='mirror')
98e38da020SMax Reitz
99e38da020SMax Reitz        # Now start some final requests; all of these (which land on
100e38da020SMax Reitz        # the source) should be settled using the active mechanism.
101e38da020SMax Reitz        # The mirror code itself asserts that the source BDS's dirty
102e38da020SMax Reitz        # bitmap will stay clean between READY and COMPLETED.
1039a3a9a63SMax Reitz        for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024):
104e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset)
1059a3a9a63SMax Reitz        for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024):
106e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset)
107e38da020SMax Reitz
108e38da020SMax Reitz        if sync_source_and_target:
109e38da020SMax Reitz            # If source and target should be in sync after the mirror,
110e38da020SMax Reitz            # we have to flush before completion
111e38da020SMax Reitz            self.vm.hmp_qemu_io('source', 'aio_flush')
112e38da020SMax Reitz            self.potential_writes_in_flight = False
113e38da020SMax Reitz
114e38da020SMax Reitz        self.complete_and_wait(drive='mirror', wait_ready=False)
115e38da020SMax Reitz
116e38da020SMax Reitz    def testActiveIO(self):
117e38da020SMax Reitz        self.doActiveIO(False)
118e38da020SMax Reitz
119e38da020SMax Reitz    def testActiveIOFlushed(self):
120e38da020SMax Reitz        self.doActiveIO(True)
121e38da020SMax Reitz
12219ba4651SMax Reitz    def testUnalignedActiveIO(self):
12319ba4651SMax Reitz        # Fill the source image
12419ba4651SMax Reitz        result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M')
12519ba4651SMax Reitz
12619ba4651SMax Reitz        # Start the block job (very slowly)
127*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-mirror',
12819ba4651SMax Reitz                    job_id='mirror',
12919ba4651SMax Reitz                    filter_node_name='mirror-node',
13019ba4651SMax Reitz                    device='source-node',
13119ba4651SMax Reitz                    target='target-node',
13219ba4651SMax Reitz                    sync='full',
13319ba4651SMax Reitz                    copy_mode='write-blocking',
13419ba4651SMax Reitz                    buf_size=(1048576 // 4),
13519ba4651SMax Reitz                    speed=1)
13619ba4651SMax Reitz
13719ba4651SMax Reitz        # Start an unaligned request to a dirty area
13819ba4651SMax Reitz        result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42))
13919ba4651SMax Reitz
14019ba4651SMax Reitz        # Let the job finish
141*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('block-job-set-speed', device='mirror', speed=0)
14219ba4651SMax Reitz        self.complete_and_wait(drive='mirror')
14319ba4651SMax Reitz
14419ba4651SMax Reitz        self.potential_writes_in_flight = False
145e38da020SMax Reitz
146e0f69d83SVladimir Sementsov-Ogievskiy    def testIntersectingActiveIO(self):
147e0f69d83SVladimir Sementsov-Ogievskiy        # Fill the source image
148e0f69d83SVladimir Sementsov-Ogievskiy        result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M')
149e0f69d83SVladimir Sementsov-Ogievskiy
150e0f69d83SVladimir Sementsov-Ogievskiy        # Start the block job (very slowly)
151*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-mirror',
152e0f69d83SVladimir Sementsov-Ogievskiy                    job_id='mirror',
153e0f69d83SVladimir Sementsov-Ogievskiy                    filter_node_name='mirror-node',
154e0f69d83SVladimir Sementsov-Ogievskiy                    device='source-node',
155e0f69d83SVladimir Sementsov-Ogievskiy                    target='target-node',
156e0f69d83SVladimir Sementsov-Ogievskiy                    sync='full',
157e0f69d83SVladimir Sementsov-Ogievskiy                    copy_mode='write-blocking',
158e0f69d83SVladimir Sementsov-Ogievskiy                    speed=1)
159e0f69d83SVladimir Sementsov-Ogievskiy
160e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'break write_aio A')
161e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 1M')  # 1
162e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'wait_break A')
163e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 2M')  # 2
164e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 0 2M')  # 3
165e0f69d83SVladimir Sementsov-Ogievskiy
166e0f69d83SVladimir Sementsov-Ogievskiy        # Now 2 and 3 are in mirror_wait_on_conflicts, waiting for 1
167e0f69d83SVladimir Sementsov-Ogievskiy
168e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'break write_aio B')
169e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'aio_write 1M 2M')  # 4
170e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'wait_break B')
171e0f69d83SVladimir Sementsov-Ogievskiy
172e0f69d83SVladimir Sementsov-Ogievskiy        # 4 doesn't wait for 2 and 3, because they didn't yet set
173e0f69d83SVladimir Sementsov-Ogievskiy        # in_flight_bitmap. So, nothing prevents 4 to go except for our
174e0f69d83SVladimir Sementsov-Ogievskiy        # break-point B.
175e0f69d83SVladimir Sementsov-Ogievskiy
176e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'resume A')
177e0f69d83SVladimir Sementsov-Ogievskiy
178e0f69d83SVladimir Sementsov-Ogievskiy        # Now we resumed 1, so 2 and 3 goes to the next iteration of while loop
179e0f69d83SVladimir Sementsov-Ogievskiy        # in mirror_wait_on_conflicts(). They don't exit, as bitmap is dirty
180d44dae1aSVladimir Sementsov-Ogievskiy        # due to request 4.
181d44dae1aSVladimir Sementsov-Ogievskiy        # In the past at that point 2 and 3 would wait for each other producing
182d44dae1aSVladimir Sementsov-Ogievskiy        # a dead-lock. Now this is fixed and they will wait for request 4.
183e0f69d83SVladimir Sementsov-Ogievskiy
184e0f69d83SVladimir Sementsov-Ogievskiy        self.vm.hmp_qemu_io('source', 'resume B')
185e0f69d83SVladimir Sementsov-Ogievskiy
186d44dae1aSVladimir Sementsov-Ogievskiy        # After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap,
187d44dae1aSVladimir Sementsov-Ogievskiy        # so the other will wait for it.
188e0f69d83SVladimir Sementsov-Ogievskiy
189*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('block-job-set-speed', device='mirror', speed=0)
190e0f69d83SVladimir Sementsov-Ogievskiy        self.complete_and_wait(drive='mirror')
191e0f69d83SVladimir Sementsov-Ogievskiy
192e0f69d83SVladimir Sementsov-Ogievskiy        self.potential_writes_in_flight = False
193e0f69d83SVladimir Sementsov-Ogievskiy
194e38da020SMax Reitz
1957b5929c7SHanna Reitzclass TestThrottledWithNbdExportBase(iotests.QMPTestCase):
19638591290SHanna Reitz    image_len = 128 * 1024 * 1024  # MB
1977b5929c7SHanna Reitz    iops: Optional[int] = None
19838591290SHanna Reitz    background_processes: List['subprocess.Popen[str]'] = []
19938591290SHanna Reitz
20038591290SHanna Reitz    def setUp(self):
2017b5929c7SHanna Reitz        # Must be set by subclasses
2027b5929c7SHanna Reitz        self.assertIsNotNone(self.iops)
2037b5929c7SHanna Reitz
20438591290SHanna Reitz        qemu_img('create', '-f', iotests.imgfmt, source_img, '128M')
20538591290SHanna Reitz        qemu_img('create', '-f', iotests.imgfmt, target_img, '128M')
20638591290SHanna Reitz
20738591290SHanna Reitz        self.vm = iotests.VM()
20838591290SHanna Reitz        self.vm.launch()
20938591290SHanna Reitz
210*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('object-add', **{
21138591290SHanna Reitz            'qom-type': 'throttle-group',
21238591290SHanna Reitz            'id': 'thrgr',
21338591290SHanna Reitz            'limits': {
21438591290SHanna Reitz                'iops-total': self.iops,
21538591290SHanna Reitz                'iops-total-max': self.iops
21638591290SHanna Reitz            }
21738591290SHanna Reitz        })
21838591290SHanna Reitz
219*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-add', **{
22038591290SHanna Reitz            'node-name': 'source-node',
22138591290SHanna Reitz            'driver': 'throttle',
22238591290SHanna Reitz            'throttle-group': 'thrgr',
22338591290SHanna Reitz            'file': {
22438591290SHanna Reitz                'driver': iotests.imgfmt,
22538591290SHanna Reitz                'file': {
22638591290SHanna Reitz                    'driver': 'file',
22738591290SHanna Reitz                    'filename': source_img
22838591290SHanna Reitz                }
22938591290SHanna Reitz            }
23038591290SHanna Reitz        })
23138591290SHanna Reitz
232*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-add', **{
23338591290SHanna Reitz            'node-name': 'target-node',
23438591290SHanna Reitz            'driver': iotests.imgfmt,
23538591290SHanna Reitz            'file': {
23638591290SHanna Reitz                'driver': 'file',
23738591290SHanna Reitz                'filename': target_img
23838591290SHanna Reitz            }
23938591290SHanna Reitz        })
24038591290SHanna Reitz
24138591290SHanna Reitz        self.nbd_sock = iotests.file_path('nbd.sock',
24238591290SHanna Reitz                                          base_dir=iotests.sock_dir)
24338591290SHanna Reitz        self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}'
24438591290SHanna Reitz
245*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('nbd-server-start', addr={
24638591290SHanna Reitz            'type': 'unix',
24738591290SHanna Reitz            'data': {
24838591290SHanna Reitz                'path': self.nbd_sock
24938591290SHanna Reitz            }
25038591290SHanna Reitz        })
25138591290SHanna Reitz
252*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('block-export-add', id='exp0', type='nbd',
25338591290SHanna Reitz                    node_name='source-node', writable=True)
25438591290SHanna Reitz
25538591290SHanna Reitz    def tearDown(self):
25638591290SHanna Reitz        # Wait for background requests to settle
25738591290SHanna Reitz        try:
25838591290SHanna Reitz            while True:
25938591290SHanna Reitz                p = self.background_processes.pop()
26038591290SHanna Reitz                while True:
26138591290SHanna Reitz                    try:
26238591290SHanna Reitz                        p.wait(timeout=0.0)
26338591290SHanna Reitz                        break
26438591290SHanna Reitz                    except subprocess.TimeoutExpired:
26538591290SHanna Reitz                        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
26638591290SHanna Reitz        except IndexError:
26738591290SHanna Reitz            pass
26838591290SHanna Reitz
26938591290SHanna Reitz        # Cancel ongoing block jobs
27038591290SHanna Reitz        for job in self.vm.qmp('query-jobs')['return']:
27138591290SHanna Reitz            self.vm.qmp('block-job-cancel', device=job['id'], force=True)
27238591290SHanna Reitz
27338591290SHanna Reitz        while True:
27438591290SHanna Reitz            self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
27538591290SHanna Reitz            if len(self.vm.qmp('query-jobs')['return']) == 0:
27638591290SHanna Reitz                break
27738591290SHanna Reitz
27838591290SHanna Reitz        self.vm.shutdown()
27938591290SHanna Reitz        os.remove(source_img)
28038591290SHanna Reitz        os.remove(target_img)
28138591290SHanna Reitz
2827b5929c7SHanna Reitz
2837b5929c7SHanna Reitzclass TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase):
2847b5929c7SHanna Reitz    iops = 16
2857b5929c7SHanna Reitz
28638591290SHanna Reitz    def testUnderLoad(self):
28738591290SHanna Reitz        '''
28838591290SHanna Reitz        Throttle the source node, then issue a whole bunch of external requests
28938591290SHanna Reitz        while the mirror job (in write-blocking mode) is running.  We want to
29038591290SHanna Reitz        see background requests being issued even while the source is under
29138591290SHanna Reitz        full load by active writes, so that progress can be made towards READY.
29238591290SHanna Reitz        '''
29338591290SHanna Reitz
29438591290SHanna Reitz        # Fill the first half of the source image; do not fill the second half,
29538591290SHanna Reitz        # that is where we will have active requests occur.  This ensures that
29638591290SHanna Reitz        # active mirroring itself will not directly contribute to the job's
29738591290SHanna Reitz        # progress (because when the job was started, those areas were not
29838591290SHanna Reitz        # intended to be copied, so active mirroring will only lead to not
29938591290SHanna Reitz        # losing progress, but also not making any).
30038591290SHanna Reitz        self.vm.hmp_qemu_io('source-node',
30138591290SHanna Reitz                            f'aio_write -P 1 0 {self.image_len // 2}')
30238591290SHanna Reitz        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
30338591290SHanna Reitz
30438591290SHanna Reitz        # Launch the mirror job
30538591290SHanna Reitz        mirror_buf_size = 65536
306*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-mirror',
30738591290SHanna Reitz                    job_id='mirror',
30838591290SHanna Reitz                    filter_node_name='mirror-node',
30938591290SHanna Reitz                    device='source-node',
31038591290SHanna Reitz                    target='target-node',
31138591290SHanna Reitz                    sync='full',
31238591290SHanna Reitz                    copy_mode='write-blocking',
31338591290SHanna Reitz                    buf_size=mirror_buf_size)
31438591290SHanna Reitz
31538591290SHanna Reitz        # We create the external requests via qemu-io processes on the NBD
31638591290SHanna Reitz        # server.  Have their offset start in the middle of the image so they
31738591290SHanna Reitz        # do not overlap with the background requests (which start from the
31838591290SHanna Reitz        # beginning).
31938591290SHanna Reitz        active_request_offset = self.image_len // 2
32038591290SHanna Reitz        active_request_len = 4096
32138591290SHanna Reitz
32238591290SHanna Reitz        # Create enough requests to saturate the node for 5 seconds
32338591290SHanna Reitz        for _ in range(0, 5 * self.iops):
32438591290SHanna Reitz            req = f'write -P 42 {active_request_offset} {active_request_len}'
32538591290SHanna Reitz            active_request_offset += active_request_len
32638591290SHanna Reitz            p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
32738591290SHanna Reitz            self.background_processes += [p]
32838591290SHanna Reitz
32938591290SHanna Reitz        # Now advance the clock one I/O operation at a time by the 4 seconds
33038591290SHanna Reitz        # (i.e. one less than 5).  We expect the mirror job to issue background
33138591290SHanna Reitz        # operations here, even though active requests are still in flight.
33238591290SHanna Reitz        # The active requests will take precedence, however, because they have
33338591290SHanna Reitz        # been issued earlier than mirror's background requests.
33438591290SHanna Reitz        # Once the active requests we have started above are done (i.e. after 5
33538591290SHanna Reitz        # virtual seconds), we expect those background requests to be worked
33638591290SHanna Reitz        # on.  We only advance 4 seconds here to avoid race conditions.
33738591290SHanna Reitz        for _ in range(0, 4 * self.iops):
33838591290SHanna Reitz            step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
33938591290SHanna Reitz            self.vm.qtest(f'clock_step {step}')
34038591290SHanna Reitz
34138591290SHanna Reitz        # Note how much remains to be done until the mirror job is finished
34238591290SHanna Reitz        job_status = self.vm.qmp('query-jobs')['return'][0]
34338591290SHanna Reitz        start_remaining = job_status['total-progress'] - \
34438591290SHanna Reitz            job_status['current-progress']
34538591290SHanna Reitz
34638591290SHanna Reitz        # Create a whole bunch of more active requests
34738591290SHanna Reitz        for _ in range(0, 10 * self.iops):
34838591290SHanna Reitz            req = f'write -P 42 {active_request_offset} {active_request_len}'
34938591290SHanna Reitz            active_request_offset += active_request_len
35038591290SHanna Reitz            p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req)
35138591290SHanna Reitz            self.background_processes += [p]
35238591290SHanna Reitz
35338591290SHanna Reitz        # Let the clock advance more.  After 1 second, as noted above, we
35438591290SHanna Reitz        # expect the background requests to be worked on.  Give them a couple
35538591290SHanna Reitz        # of seconds (specifically 4) to see their impact.
35638591290SHanna Reitz        for _ in range(0, 5 * self.iops):
35738591290SHanna Reitz            step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops)
35838591290SHanna Reitz            self.vm.qtest(f'clock_step {step}')
35938591290SHanna Reitz
36038591290SHanna Reitz        # Note how much remains to be done now.  We expect this number to be
36138591290SHanna Reitz        # reduced thanks to those background requests.
36238591290SHanna Reitz        job_status = self.vm.qmp('query-jobs')['return'][0]
36338591290SHanna Reitz        end_remaining = job_status['total-progress'] - \
36438591290SHanna Reitz            job_status['current-progress']
36538591290SHanna Reitz
36638591290SHanna Reitz        # See that indeed progress was being made on the job, even while the
36738591290SHanna Reitz        # node was saturated with active requests
36838591290SHanna Reitz        self.assertGreater(start_remaining - end_remaining, 0)
36938591290SHanna Reitz
37038591290SHanna Reitz
3717b5929c7SHanna Reitzclass TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase):
3727b5929c7SHanna Reitz    iops = 1024
3737b5929c7SHanna Reitz
3747b5929c7SHanna Reitz    def testActiveOnCreation(self):
3757b5929c7SHanna Reitz        '''
3767b5929c7SHanna Reitz        Issue requests on the mirror source node right as the mirror is
3777b5929c7SHanna Reitz        instated.  It's possible that requests occur before the actual job is
3787b5929c7SHanna Reitz        created, but after the node has been put into the graph.  Write
3797b5929c7SHanna Reitz        requests across the node must in that case be forwarded to the source
3807b5929c7SHanna Reitz        node without attempting to mirror them (there is no job object yet, so
3817b5929c7SHanna Reitz        attempting to access it would cause a segfault).
3827b5929c7SHanna Reitz        We do this with a lightly throttled node (i.e. quite high IOPS limit).
3837b5929c7SHanna Reitz        Using throttling seems to increase reproductivity, but if the limit is
3847b5929c7SHanna Reitz        too low, all requests allowed per second will be submitted before
3857b5929c7SHanna Reitz        mirror_start_job() gets to the problematic point.
3867b5929c7SHanna Reitz        '''
3877b5929c7SHanna Reitz
3887b5929c7SHanna Reitz        # Let qemu-img bench create write requests (enough for two seconds on
3897b5929c7SHanna Reitz        # the virtual clock)
3907b5929c7SHanna Reitz        bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd',
3917b5929c7SHanna Reitz                      '-c', str(self.iops * 2), self.nbd_url]
3927b5929c7SHanna Reitz        p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args)
3937b5929c7SHanna Reitz        self.background_processes += [p]
3947b5929c7SHanna Reitz
3957b5929c7SHanna Reitz        # Give qemu-img bench time to start up and issue requests
3967b5929c7SHanna Reitz        time.sleep(1.0)
3977b5929c7SHanna Reitz        # Flush the request queue, so new requests can come in right as we
3987b5929c7SHanna Reitz        # start blockdev-mirror
3997b5929c7SHanna Reitz        self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}')
4007b5929c7SHanna Reitz
401*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-mirror',
4027b5929c7SHanna Reitz                    job_id='mirror',
4037b5929c7SHanna Reitz                    device='source-node',
4047b5929c7SHanna Reitz                    target='target-node',
4057b5929c7SHanna Reitz                    sync='full',
4067b5929c7SHanna Reitz                    copy_mode='write-blocking')
4077b5929c7SHanna Reitz
4087b5929c7SHanna Reitz
409e38da020SMax Reitzif __name__ == '__main__':
410103cbc77SMax Reitz    iotests.main(supported_fmts=['qcow2', 'raw'],
411103cbc77SMax Reitz                 supported_protocols=['file'])
412