iotests: Test commit job start with concurrent I/O

This tests that concurrent requests are correctly drained before making
graph modifications instead of running into assertions in
bdrv_replace_node().

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Kevin Wolf 2019-05-21 20:35:52 +02:00
parent f871abd60f
commit ac6fb43eae
4 changed files with 109 additions and 1 deletions

83
tests/qemu-iotests/255 Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python
#
# Test commit job graph modifications while requests are active
#
# Copyright (C) 2019 Red Hat, Inc.
#
# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import iotests
from iotests import imgfmt
iotests.verify_image_format(supported_fmts=['qcow2'])
def blockdev_create(vm, options):
result = vm.qmp_log('blockdev-create',
filters=[iotests.filter_qmp_testfiles],
job_id='job0', options=options)
if 'return' in result:
assert result['return'] == {}
vm.run_job('job0')
iotests.log("")
with iotests.FilePath('t.qcow2') as disk_path, \
iotests.FilePath('t.qcow2.mid') as mid_path, \
iotests.FilePath('t.qcow2.base') as base_path, \
iotests.VM() as vm:
iotests.log("=== Create backing chain and start VM ===")
iotests.log("")
size = 128 * 1024 * 1024
size_str = str(size)
iotests.create_image(base_path, size)
iotests.qemu_img_log('create', '-f', iotests.imgfmt, mid_path, size_str)
iotests.qemu_img_log('create', '-f', iotests.imgfmt, disk_path, size_str)
# Create a backing chain like this:
# base <- [throttled: bps-read=4096] <- mid <- overlay
vm.add_object('throttle-group,x-bps-read=4096,id=throttle0')
vm.add_blockdev('file,filename=%s,node-name=base' % (base_path))
vm.add_blockdev('throttle,throttle-group=throttle0,file=base,node-name=throttled')
vm.add_blockdev('file,filename=%s,node-name=mid-file' % (mid_path))
vm.add_blockdev('qcow2,file=mid-file,node-name=mid,backing=throttled')
vm.add_drive_raw('if=none,id=overlay,driver=qcow2,file=%s,backing=mid' % (disk_path))
vm.launch()
iotests.log("=== Start background read requests ===")
iotests.log("")
def start_requests():
vm.hmp_qemu_io('overlay', 'aio_read 0 4k')
vm.hmp_qemu_io('overlay', 'aio_read 0 4k')
start_requests()
iotests.log("=== Run a commit job ===")
iotests.log("")
result = vm.qmp_log('block-commit', job_id='job0', auto_finalize=False,
device='overlay', top_node='mid')
vm.run_job('job0', auto_finalize=False, pre_finalize=start_requests,
auto_dismiss=True)
vm.shutdown()

View File

@ -0,0 +1,16 @@
=== Create backing chain and start VM ===
Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 size=134217728 cluster_size=65536 lazy_refcounts=off refcount_bits=16
=== Start background read requests ===
=== Run a commit job ===
{"execute": "block-commit", "arguments": {"auto-finalize": false, "device": "overlay", "job-id": "job0", "top-node": "mid"}}
{"return": {}}
{"execute": "job-finalize", "arguments": {"id": "job0"}}
{"return": {}}
{"data": {"id": "job0", "type": "commit"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}

View File

@ -265,3 +265,4 @@
252 rw auto backing quick 252 rw auto backing quick
253 rw auto quick 253 rw auto quick
254 rw auto backing quick 254 rw auto backing quick
255 rw auto quick

View File

@ -126,6 +126,11 @@ def qemu_img_pipe(*args):
sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
return subp.communicate()[0] return subp.communicate()[0]
def qemu_img_log(*args):
result = qemu_img_pipe(*args)
log(result, filters=[filter_testfiles])
return result
def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
args = [ 'info' ] args = [ 'info' ]
if imgopts: if imgopts:
@ -533,7 +538,8 @@ class VM(qtest.QEMUQtestMachine):
return result return result
# Returns None on success, and an error string on failure # Returns None on success, and an error string on failure
def run_job(self, job, auto_finalize=True, auto_dismiss=False): def run_job(self, job, auto_finalize=True, auto_dismiss=False,
pre_finalize=None):
error = None error = None
while True: while True:
for ev in self.get_qmp_events_filtered(wait=True): for ev in self.get_qmp_events_filtered(wait=True):
@ -546,6 +552,8 @@ class VM(qtest.QEMUQtestMachine):
error = j['error'] error = j['error']
log('Job failed: %s' % (j['error'])) log('Job failed: %s' % (j['error']))
elif status == 'pending' and not auto_finalize: elif status == 'pending' and not auto_finalize:
if pre_finalize:
pre_finalize()
self.qmp_log('job-finalize', id=job) self.qmp_log('job-finalize', id=job)
elif status == 'concluded' and not auto_dismiss: elif status == 'concluded' and not auto_dismiss:
self.qmp_log('job-dismiss', id=job) self.qmp_log('job-dismiss', id=job)