blockdev-backup: Add error handling option for copy-before-write jobs

This patch extends the blockdev-backup QMP command to allow users to specify
how to behave when IO errors occur during copy-before-write operations.
Previously, the behavior was fixed and could not be controlled by the user.

The new 'on-cbw-error' option can be set to one of two values:
- 'break-guest-write': Forwards the IO error to the guest and triggers
  the on-source-error policy. This preserves snapshot integrity at the
  expense of guest IO operations.
- 'break-snapshot': Allows the guest OS to continue running normally,
  but invalidates the snapshot and aborts related jobs. This prioritizes
  guest operation over backup consistency.

This enhancement provides more flexibility for backup operations in different
environments where requirements for guest availability versus backup
consistency may vary.

The default behavior remains unchanged to maintain backward compatibility.

Signed-off-by: Raman Dzehtsiar <Raman.Dzehtsiar@gmail.com>
Message-ID: <20250414090025.828660-1-Raman.Dzehtsiar@gmail.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
[vsementsov: fix long lines]
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
This commit is contained in:
Raman Dzehtsiar 2025-04-14 11:00:25 +02:00 committed by Vladimir Sementsov-Ogievskiy
parent b836bf2ab6
commit 3d3911f16b
9 changed files with 117 additions and 4 deletions

View file

@ -99,6 +99,68 @@ class TestCbwError(iotests.QMPTestCase):
log = iotests.filter_qemu_io(log)
return log
def do_cbw_error_via_blockdev_backup(self, on_cbw_error=None):
self.vm.cmd('blockdev-add', {
'node-name': 'source',
'driver': iotests.imgfmt,
'file': {
'driver': 'file',
'filename': source_img
}
})
self.vm.cmd('blockdev-add', {
'node-name': 'target',
'driver': iotests.imgfmt,
'file': {
'driver': 'blkdebug',
'image': {
'driver': 'file',
'filename': temp_img
},
'inject-error': [
{
'event': 'write_aio',
'errno': 5,
'immediately': False,
'once': True
}
]
}
})
blockdev_backup_options = {
'device': 'source',
'target': 'target',
'sync': 'none',
'job-id': 'job-id',
'filter-node-name': 'cbw'
}
if on_cbw_error:
blockdev_backup_options['on-cbw-error'] = on_cbw_error
self.vm.cmd('blockdev-backup', blockdev_backup_options)
self.vm.cmd('blockdev-add', {
'node-name': 'access',
'driver': 'snapshot-access',
'file': 'cbw'
})
result = self.vm.qmp('human-monitor-command',
command_line='qemu-io cbw "write 0 1M"')
self.assert_qmp(result, 'return', '')
result = self.vm.qmp('human-monitor-command',
command_line='qemu-io access "read 0 1M"')
self.assert_qmp(result, 'return', '')
self.vm.shutdown()
log = self.vm.get_log()
log = iotests.filter_qemu_io(log)
return log
def test_break_snapshot_on_cbw_error(self):
"""break-snapshot behavior:
Guest write succeed, but further snapshot-read fails, as snapshot is
@ -123,6 +185,39 @@ read failed: Permission denied
write failed: Input/output error
read 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
""")
def test_break_snapshot_policy_forwarding(self):
"""Ensure CBW filter accepts break-snapshot policy
specified in blockdev-backup QMP command.
"""
log = self.do_cbw_error_via_blockdev_backup('break-snapshot')
self.assertEqual(log, """\
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read failed: Permission denied
""")
def test_break_guest_write_policy_forwarding(self):
"""Ensure CBW filter accepts break-guest-write policy
specified in blockdev-backup QMP command.
"""
log = self.do_cbw_error_via_blockdev_backup('break-guest-write')
self.assertEqual(log, """\
write failed: Input/output error
read 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
""")
def test_default_on_cbw_error_policy_forwarding(self):
"""Ensure break-guest-write policy is used by default when
on-cbw-error is not explicitly specified.
"""
log = self.do_cbw_error_via_blockdev_backup()
self.assertEqual(log, """\
write failed: Input/output error
read 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
""")
def do_cbw_timeout(self, on_cbw_error):

View file

@ -1,5 +1,5 @@
....
.......
----------------------------------------------------------------------
Ran 4 tests
Ran 7 tests
OK