qemu/tests/qemu-iotests/194
Eric Blake 181a63667a mirror: Skip pre-zeroing destination if it is already zero
When doing a sync=full mirroring, we can skip pre-zeroing the
destination if it already reads as zeroes and we are not also trying
to punch holes due to detect-zeroes.  With this patch, there are fewer
scenarios that have to pass in an explicit target-is-zero, while still
resulting in a sparse destination remaining sparse.

A later patch will then further improve things to skip writing to the
destination for parts of the image where the source is zero; but even
with just this patch, it is possible to see a difference for any
source that does not report itself as fully allocated, coupled with a
destination BDS that can quickly report that it already reads as zero.
(For a source that reports as fully allocated, such as a file, the
rest of mirror_dirty_init() still sets the entire dirty bitmap to
true, so even though we avoided the pre-zeroing, we are not yet
avoiding all redundant I/O).

Iotest 194 detects the difference made by this patch: for a file
source (where block status reports the entire image as allocated, and
therefore we end up writing zeroes everywhere in the destination
anyways), the job length remains the same.  But for a qcow2 source and
a destination that reads as all zeroes, the dirty bitmap changes to
just tracking the allocated portions of the source, which results in
faster completion and smaller job statistics.  For the test to pass
with both ./check -file and -qcow2, a new python filter is needed to
mask out the now-varying job amounts (this matches the shell filters
_filter_block_job_{offset,len} in common.filter).  A later test will
also be added which further validates expected sparseness, so it does
not matter that 194 is no longer explicitly looking at how many bytes
were copied.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20250509204341.3553601-25-eblake@redhat.com>
Reviewed-by: Sunny Zhu <sunnyzhyy@qq.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2025-05-14 20:20:14 -05:00

109 lines
4.7 KiB
Python
Executable file

#!/usr/bin/env python3
# group: rw migration quick
#
# Copyright (C) 2017 Red Hat, Inc.
#
# 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/>.
#
# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
#
# Non-shared storage migration test using NBD server and drive-mirror
import iotests
iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'],
supported_platforms=['linux'])
with iotests.FilePath('source.img') as source_img_path, \
iotests.FilePath('dest.img') as dest_img_path, \
iotests.FilePath('migration.sock', 'nbd.sock', base_dir=iotests.sock_dir) \
as (migration_sock_path, nbd_sock_path), \
iotests.VM('source') as source_vm, \
iotests.VM('dest') as dest_vm:
img_size = '1G'
iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size)
iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 512M 1M', source_img_path)
iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size)
iotests.log('Launching VMs...')
(source_vm.add_drive(source_img_path)
.launch())
(dest_vm.add_drive(dest_img_path)
.add_incoming('unix:{0}'.format(migration_sock_path))
.launch())
source_vm.qmp_log('block-dirty-bitmap-add', node='drive0', name='bitmap0')
iotests.log('Launching NBD server on destination...')
iotests.log(dest_vm.qmp('nbd-server-start', addr={'type': 'unix', 'data': {'path': nbd_sock_path}}))
iotests.log(dest_vm.qmp('nbd-server-add', device='drive0', writable=True))
iotests.log('Starting `drive-mirror` on source...')
iotests.log(source_vm.qmp(
'drive-mirror',
device='drive0',
target='nbd+unix:///drive0?socket={0}'.format(nbd_sock_path),
sync='full',
format='raw', # always raw, the server handles the format
mode='existing',
job_id='mirror-job0'))
iotests.log('Waiting for `drive-mirror` to complete...')
iotests.log(source_vm.event_wait('BLOCK_JOB_READY'),
filters=[iotests.filter_qmp_event,
iotests.filter_block_job])
iotests.log('Starting migration...')
capabilities = [{'capability': 'events', 'state': True},
{'capability': 'dirty-bitmaps', 'state': True}]
source_vm.qmp('migrate-set-capabilities', capabilities=capabilities)
dest_vm.qmp('migrate-set-capabilities', capabilities=capabilities)
iotests.log(source_vm.qmp('migrate', uri='unix:{0}'.format(migration_sock_path)))
source_vm.qmp_log('migrate-start-postcopy')
while True:
event1 = source_vm.event_wait('MIGRATION')
if event1['data']['status'] == 'postcopy-active':
# This event is racy, it depends do we really do postcopy or bitmap
# was migrated during downtime (and no data to migrate in postcopy
# phase). So, don't log it.
continue
iotests.log(event1, filters=[iotests.filter_qmp_event])
if event1['data']['status'] in ('completed', 'failed'):
iotests.log('Gracefully ending the `drive-mirror` job on source...')
iotests.log(source_vm.qmp('block-job-cancel', device='mirror-job0'))
break
while True:
event2 = source_vm.event_wait('BLOCK_JOB_COMPLETED')
iotests.log(event2, filters=[iotests.filter_qmp_event,
iotests.filter_block_job])
if event2['event'] == 'BLOCK_JOB_COMPLETED':
iotests.log('Stopping the NBD server on destination...')
iotests.log(dest_vm.qmp('nbd-server-stop'))
break
iotests.log('Wait for migration completion on target...')
migr_events = (('MIGRATION', {'data': {'status': 'completed'}}),
('MIGRATION', {'data': {'status': 'failed'}}))
event = dest_vm.events_wait(migr_events)
iotests.log(event, filters=[iotests.filter_qmp_event])
iotests.log('Check bitmaps on source:')
iotests.log(source_vm.qmp('query-block')['return'][0]['inserted']['dirty-bitmaps'])
iotests.log('Check bitmaps on target:')
iotests.log(dest_vm.qmp('query-block')['return'][0]['inserted']['dirty-bitmaps'])