Add a regression test for the BlockBackend reference leak in
scsi_cd_realize(): create an empty scsi-cd (which gets an anonymous
BlockBackend), insert a medium, unplug the device, and check that the
medium's blockdev node can then be deleted.  Before the fix the leaked
BlockBackend keeps the node in use and blockdev-del fails.

Signed-off-by: Mitsuru Kariya <[email protected]>
---
 tests/qtest/drive_del-test.c | 58 ++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c
index 30d9451ddd..24793da852 100644
--- a/tests/qtest/drive_del-test.c
+++ b/tests/qtest/drive_del-test.c
@@ -320,6 +320,62 @@ static void test_empty_device_del(void)
     qtest_quit(qts);
 }
 
+static void test_insert_medium_and_device_del(void)
+{
+    QTestState *qts;
+    QDict *response;
+
+    if (!has_device_builtin("virtio-scsi")) {
+        g_test_skip("Device virtio-scsi is not available");
+        return;
+    }
+
+    /* An empty scsi-cd creates an anonymous BlockBackend at realize. */
+    qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0",
+                      qvirtio_get_dev_type());
+
+    /* Create the blockdev node to be inserted as a medium. */
+    blockdev_add_with_media(qts);
+
+    /* Open tray. */
+    response = qtest_qmp(qts, "{'execute': 'blockdev-open-tray',"
+                              " 'arguments': {'id': 'dev0'}}");
+    g_assert(response);
+    g_assert(qdict_haskey(response, "return"));
+    qobject_unref(response);
+
+    /*
+     * Insert a medium into the empty drive.  This adds a reference from
+     * the anonymous BlockBackend to the blockdev node.
+     */
+    response = qtest_qmp(qts, "{'execute': 'blockdev-insert-medium',"
+                              " 'arguments': {'id': 'dev0',"
+                              "               'node-name': 'drive0'}}");
+    g_assert(response);
+    g_assert(qdict_haskey(response, "return"));
+    qobject_unref(response);
+
+    /*
+     * Unplug the device.  This drops the last reference to the anonymous
+     * BlockBackend, so it is deleted and releases its reference to the
+     * blockdev node.
+     */
+    device_del(qts, false);
+
+    /*
+     * If the BlockBackend leaked, drive0 is still in use here and
+     * blockdev-del fails.
+     */
+    response = qtest_qmp(qts, "{'execute': 'blockdev-del',"
+                              " 'arguments': {'node-name': 'drive0'}}");
+    g_assert(response);
+    g_assert(qdict_haskey(response, "return"));
+    qobject_unref(response);
+    g_assert(!has_blockdev(qts));
+
+    qtest_quit(qts);
+}
+
 static void test_device_add_and_del(void)
 {
     QTestState *qts;
@@ -516,6 +572,8 @@ int main(int argc, char **argv)
                        test_drive_add_device_add_and_del);
         qtest_add_func("/device_del/empty",
                        test_empty_device_del);
+        qtest_add_func("/device_del/insert_medium",
+                       test_insert_medium_and_device_del);
         qtest_add_func("/device_del/blockdev",
                        test_blockdev_add_device_add_and_del);
 
-- 
2.43.0


Reply via email to