2bc292
From 3d2d7a46713d362d2ff5137841e689593da976a3 Mon Sep 17 00:00:00 2001
2bc292
From: Hanna Reitz <hreitz@redhat.com>
2bc292
Date: Fri, 4 Feb 2022 12:10:10 +0100
2bc292
Subject: [PATCH 6/8] iotests/281: Test lingering timers
2bc292
2bc292
RH-Author: Hanna Reitz <hreitz@redhat.com>
2bc292
RH-MergeRequest: 74: block/nbd: Handle AioContext changes
2bc292
RH-Commit: [4/6] d228ba3fcdfaab2d54dd5b023688a1c055cce2c2 (hreitz/qemu-kvm-c-9-s)
2bc292
RH-Bugzilla: 2033626
2bc292
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
2bc292
RH-Acked-by: Eric Blake <eblake@redhat.com>
2bc292
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
2bc292
2bc292
Prior to "block/nbd: Delete reconnect delay timer when done" and
2bc292
"block/nbd: Delete open timer when done", both of those timers would
2bc292
remain scheduled even after successfully (re-)connecting to the server,
2bc292
and they would not even be deleted when the BDS is deleted.
2bc292
2bc292
This test constructs exactly this situation:
2bc292
(1) Configure an @open-timeout, so the open timer is armed, and
2bc292
(2) Configure a @reconnect-delay and trigger a reconnect situation
2bc292
    (which succeeds immediately), so the reconnect delay timer is armed.
2bc292
Then we immediately delete the BDS, and sleep for longer than the
2bc292
@open-timeout and @reconnect-delay.  Prior to said patches, this caused
2bc292
one (or both) of the timer CBs to access already-freed data.
2bc292
2bc292
Accessing freed data may or may not crash, so this test can produce
2bc292
false successes, but I do not know how to show the problem in a better
2bc292
or more reliable way.  If you run this test on "block/nbd: Assert there
2bc292
are no timers when closed" and without the fix patches mentioned above,
2bc292
you should reliably see an assertion failure.
2bc292
(But all other tests that use the reconnect delay timer (264 and 277)
2bc292
will fail in that configuration, too; as will nbd-reconnect-on-open,
2bc292
which uses the open timer.)
2bc292
2bc292
Remove this test from the quick group because of the two second sleep
2bc292
this patch introduces.
2bc292
2bc292
(I decided to put this test case into 281, because the main bug this
2bc292
series addresses is in the interaction of the NBD block driver and I/O
2bc292
threads, which is precisely the scope of 281.  The test case for that
2bc292
other bug will also be put into the test class added here.
2bc292
2bc292
Also, excuse the test class's name, I couldn't come up with anything
2bc292
better.  The "yield" part will make sense two patches from now.)
2bc292
2bc292
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2bc292
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
2bc292
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2bc292
(cherry picked from commit eaf1e85d4ddefdbd197f393fa9c5acc7ba8133b0)
2bc292
2bc292
Conflict:
2bc292
- @open-timeout was introduced after the 6.2 release, and has not been
2bc292
  backported.  Consequently, there is no open_timer, and we can (and
2bc292
  must) drop the respective parts of the test here.
2bc292
2bc292
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
2bc292
---
2bc292
 tests/qemu-iotests/281     | 73 ++++++++++++++++++++++++++++++++++++--
2bc292
 tests/qemu-iotests/281.out |  4 +--
2bc292
 2 files changed, 73 insertions(+), 4 deletions(-)
2bc292
2bc292
diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281
2bc292
index 956698083f..13c588be75 100755
2bc292
--- a/tests/qemu-iotests/281
2bc292
+++ b/tests/qemu-iotests/281
2bc292
@@ -1,5 +1,5 @@
2bc292
 #!/usr/bin/env python3
2bc292
-# group: rw quick
2bc292
+# group: rw
2bc292
 #
2bc292
 # Test cases for blockdev + IOThread interactions
2bc292
 #
2bc292
@@ -20,8 +20,9 @@
2bc292
 #
2bc292
 
2bc292
 import os
2bc292
+import time
2bc292
 import iotests
2bc292
-from iotests import qemu_img
2bc292
+from iotests import qemu_img, QemuStorageDaemon
2bc292
 
2bc292
 image_len = 64 * 1024 * 1024
2bc292
 
2bc292
@@ -243,6 +244,74 @@ class TestBlockdevBackupAbort(iotests.QMPTestCase):
2bc292
         # Hangs on failure, we expect this error.
2bc292
         self.assert_qmp(result, 'error/class', 'GenericError')
2bc292
 
2bc292
+# Test for RHBZ#2033626
2bc292
+class TestYieldingAndTimers(iotests.QMPTestCase):
2bc292
+    sock = os.path.join(iotests.sock_dir, 'nbd.sock')
2bc292
+    qsd = None
2bc292
+
2bc292
+    def setUp(self):
2bc292
+        self.create_nbd_export()
2bc292
+
2bc292
+        # Simple VM with an NBD block device connected to the NBD export
2bc292
+        # provided by the QSD
2bc292
+        self.vm = iotests.VM()
2bc292
+        self.vm.add_blockdev('nbd,node-name=nbd,server.type=unix,' +
2bc292
+                             f'server.path={self.sock},export=exp,' +
2bc292
+                             'reconnect-delay=1')
2bc292
+
2bc292
+        self.vm.launch()
2bc292
+
2bc292
+    def tearDown(self):
2bc292
+        self.stop_nbd_export()
2bc292
+        self.vm.shutdown()
2bc292
+
2bc292
+    def test_timers_with_blockdev_del(self):
2bc292
+        # Stop and restart the NBD server, and do some I/O on the client to
2bc292
+        # trigger a reconnect and start the reconnect delay timer
2bc292
+        self.stop_nbd_export()
2bc292
+        self.create_nbd_export()
2bc292
+
2bc292
+        result = self.vm.qmp('human-monitor-command',
2bc292
+                             command_line='qemu-io nbd "write 0 512"')
2bc292
+        self.assert_qmp(result, 'return', '')
2bc292
+
2bc292
+        # Reconnect is done, so the reconnect delay timer should be gone.
2bc292
+        # (But there used to be a bug where it remained active, for which this
2bc292
+        # is a regression test.)
2bc292
+
2bc292
+        # Delete the BDS to see whether the timer is gone.  If it is not,
2bc292
+        # it will remain active, fire later, and then access freed data.
2bc292
+        # (Or, with "block/nbd: Assert there are no timers when closed"
2bc292
+        # applied, the assertion added in that patch will fail.)
2bc292
+        result = self.vm.qmp('blockdev-del', node_name='nbd')
2bc292
+        self.assert_qmp(result, 'return', {})
2bc292
+
2bc292
+        # Give the timer some time to fire (it has a timeout of 1 s).
2bc292
+        # (Sleeping in an iotest may ring some alarm bells, but note that if
2bc292
+        # the timing is off here, the test will just always pass.  If we kill
2bc292
+        # the VM too early, then we just kill the timer before it can fire,
2bc292
+        # thus not see the error, and so the test will pass.)
2bc292
+        time.sleep(2)
2bc292
+
2bc292
+    def create_nbd_export(self):
2bc292
+        assert self.qsd is None
2bc292
+
2bc292
+        # Simple NBD export of a null-co BDS
2bc292
+        self.qsd = QemuStorageDaemon(
2bc292
+            '--blockdev',
2bc292
+            'null-co,node-name=null,read-zeroes=true',
2bc292
+
2bc292
+            '--nbd-server',
2bc292
+            f'addr.type=unix,addr.path={self.sock}',
2bc292
+
2bc292
+            '--export',
2bc292
+            'nbd,id=exp,node-name=null,name=exp,writable=true'
2bc292
+        )
2bc292
+
2bc292
+    def stop_nbd_export(self):
2bc292
+        self.qsd.stop()
2bc292
+        self.qsd = None
2bc292
+
2bc292
 if __name__ == '__main__':
2bc292
     iotests.main(supported_fmts=['qcow2'],
2bc292
                  supported_protocols=['file'])
2bc292
diff --git a/tests/qemu-iotests/281.out b/tests/qemu-iotests/281.out
2bc292
index 89968f35d7..914e3737bd 100644
2bc292
--- a/tests/qemu-iotests/281.out
2bc292
+++ b/tests/qemu-iotests/281.out
2bc292
@@ -1,5 +1,5 @@
2bc292
-....
2bc292
+.....
2bc292
 ----------------------------------------------------------------------
2bc292
-Ran 4 tests
2bc292
+Ran 5 tests
2bc292
 
2bc292
 OK
2bc292
-- 
2bc292
2.27.0
2bc292