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