From: Haoqian He <[email protected]>

A mirror_top node without BDRV_O_UNMAP silently ignores discard
requests.

Add a regression test to verify that discard requests are not
ignored during background mirror.

Signed-off-by: Haoqian He <[email protected]>
Message-ID: <[email protected]>
Reviewed-by: Kevin Wolf <[email protected]>
Signed-off-by: Kevin Wolf <[email protected]>
---
 tests/qemu-iotests/tests/mirror-discard     | 73 +++++++++++++++++++++
 tests/qemu-iotests/tests/mirror-discard.out |  5 ++
 2 files changed, 78 insertions(+)
 create mode 100755 tests/qemu-iotests/tests/mirror-discard
 create mode 100644 tests/qemu-iotests/tests/mirror-discard.out

diff --git a/tests/qemu-iotests/tests/mirror-discard 
b/tests/qemu-iotests/tests/mirror-discard
new file mode 100755
index 00000000000..4b89e9bc7dc
--- /dev/null
+++ b/tests/qemu-iotests/tests/mirror-discard
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# group: rw auto quick
+#
+# Test that discard reaches the source through a blockdev-mirror filter.
+#
+# Copyright (C) 2026 SmartX, Inc.
+
+from pathlib import Path
+
+import iotests
+
+
+source = Path(iotests.test_dir, 'mirror-discard-source.qcow2')
+target = Path(iotests.test_dir, 'mirror-discard-target.qcow2')
+
+
+class TestMirrorDiscard(iotests.QMPTestCase):
+    def setUp(self):
+        for path in (source, target):
+            self.addCleanup(path.unlink, missing_ok=True)
+            iotests.qemu_img_create(
+                '-f', iotests.imgfmt,
+                '-o', 'compat=1.1,cluster_size=65536', str(path), '1M')
+
+        self.vm = iotests.VM()
+        # Cleanups run in reverse order: close the VM before removing images.
+        self.addCleanup(self.vm.shutdown)
+        self.vm.add_drive(str(source), opts='discard=unmap,node-name=src')
+        self.vm.add_blockdev(
+            f'driver={iotests.imgfmt},node-name=target,'
+            f'file.driver=file,file.filename={target}')
+        self.vm.launch()
+
+    def test_background_discard(self):
+        self.vm.cmd('x-qemu-io', device='drive0',
+                    command='write -q -P 0xab 0 192k')
+
+        # Check discard works before mirror_top is inserted, then restore data.
+        self.vm.cmd('x-qemu-io', device='drive0',
+                    command='discard -q 64k 64k')
+        self.vm.cmd('x-qemu-io', device='drive0',
+                    command='read -q -P 0x00 64k 64k')
+        self.vm.cmd('x-qemu-io', device='drive0',
+                    command='write -q -P 0xab 64k 64k')
+
+        self.vm.cmd('blockdev-mirror', job_id='mirror', device='drive0',
+                    target='target', sync='full', copy_mode='background')
+        self.wait_ready('mirror')
+        self.pause_job('mirror')
+
+        # Use the backend so the request passes through mirror_top to src.
+        self.vm.cmd('x-qemu-io', device='drive0',
+                    command='discard -q 64k 64k')
+        checks = (
+            'read -q -P 0x00 64k 64k',
+            'read -q -P 0xab 0 64k',
+            'read -q -P 0xab 128k 64k',
+        )
+        for command in checks:
+            self.vm.cmd('x-qemu-io', device='drive0', command=command)
+
+        self.vm.cmd('block-job-resume', device='mirror')
+        self.complete_and_wait('mirror', wait_ready=False)
+        self.vm.shutdown()
+
+        # Check both images independently after all writes have been flushed.
+        for path in (source, target):
+            for command in checks:
+                iotests.qemu_io('-f', iotests.imgfmt, '-c', command, str(path))
+
+
+if __name__ == '__main__':
+    iotests.main(supported_fmts=['qcow2'], supported_protocols=['file'])
diff --git a/tests/qemu-iotests/tests/mirror-discard.out 
b/tests/qemu-iotests/tests/mirror-discard.out
new file mode 100644
index 00000000000..ae1213e6f86
--- /dev/null
+++ b/tests/qemu-iotests/tests/mirror-discard.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
-- 
2.56.0


Reply via email to