Blame SOURCES/kvm-test-bdrv-drain-Add-test-for-node-deletion.patch

1bdc94
From e830be5d2de1d07f54b741f7a6e8bdea357bf044 Mon Sep 17 00:00:00 2001
1bdc94
From: Kevin Wolf <kwolf@redhat.com>
1bdc94
Date: Fri, 14 Sep 2018 10:55:07 +0200
1bdc94
Subject: [PATCH 16/49] test-bdrv-drain: Add test for node deletion
1bdc94
1bdc94
RH-Author: Kevin Wolf <kwolf@redhat.com>
1bdc94
Message-id: <20180914105540.18077-10-kwolf@redhat.com>
1bdc94
Patchwork-id: 82162
1bdc94
O-Subject: [RHV-7.6 qemu-kvm-rhev PATCH 09/42] test-bdrv-drain: Add test for node deletion
1bdc94
Bugzilla: 1601212
1bdc94
RH-Acked-by: John Snow <jsnow@redhat.com>
1bdc94
RH-Acked-by: Max Reitz <mreitz@redhat.com>
1bdc94
RH-Acked-by: Fam Zheng <famz@redhat.com>
1bdc94
1bdc94
From: Max Reitz <mreitz@redhat.com>
1bdc94
1bdc94
This patch adds two bdrv-drain tests for what happens if some BDS goes
1bdc94
away during the drainage.
1bdc94
1bdc94
The basic idea is that you have a parent BDS with some child nodes.
1bdc94
Then, you drain one of the children.  Because of that, the party who
1bdc94
actually owns the parent decides to (A) delete it, or (B) detach all its
1bdc94
children from it -- both while the child is still being drained.
1bdc94
1bdc94
A real-world case where this can happen is the mirror block job, which
1bdc94
may exit if you drain one of its children.
1bdc94
1bdc94
Signed-off-by: Max Reitz <mreitz@redhat.com>
1bdc94
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1bdc94
(cherry picked from commit 4c8158e359d194394c64acd21caf5e3f3f3141c2)
1bdc94
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1bdc94
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
1bdc94
---
1bdc94
 tests/test-bdrv-drain.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++
1bdc94
 1 file changed, 169 insertions(+)
1bdc94
1bdc94
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
1bdc94
index 49786ea..8918a94 100644
1bdc94
--- a/tests/test-bdrv-drain.c
1bdc94
+++ b/tests/test-bdrv-drain.c
1bdc94
@@ -792,6 +792,172 @@ static void test_blockjob_drain_subtree(void)
1bdc94
     test_blockjob_common(BDRV_SUBTREE_DRAIN);
1bdc94
 }
1bdc94
 
1bdc94
+
1bdc94
+typedef struct BDRVTestTopState {
1bdc94
+    BdrvChild *wait_child;
1bdc94
+} BDRVTestTopState;
1bdc94
+
1bdc94
+static void bdrv_test_top_close(BlockDriverState *bs)
1bdc94
+{
1bdc94
+    BdrvChild *c, *next_c;
1bdc94
+    QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
1bdc94
+        bdrv_unref_child(bs, c);
1bdc94
+    }
1bdc94
+}
1bdc94
+
1bdc94
+static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs,
1bdc94
+                                                uint64_t offset, uint64_t bytes,
1bdc94
+                                                QEMUIOVector *qiov, int flags)
1bdc94
+{
1bdc94
+    BDRVTestTopState *tts = bs->opaque;
1bdc94
+    return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags);
1bdc94
+}
1bdc94
+
1bdc94
+static BlockDriver bdrv_test_top_driver = {
1bdc94
+    .format_name            = "test_top_driver",
1bdc94
+    .instance_size          = sizeof(BDRVTestTopState),
1bdc94
+
1bdc94
+    .bdrv_close             = bdrv_test_top_close,
1bdc94
+    .bdrv_co_preadv         = bdrv_test_top_co_preadv,
1bdc94
+
1bdc94
+    .bdrv_child_perm        = bdrv_format_default_perms,
1bdc94
+};
1bdc94
+
1bdc94
+typedef struct TestCoDeleteByDrainData {
1bdc94
+    BlockBackend *blk;
1bdc94
+    bool detach_instead_of_delete;
1bdc94
+    bool done;
1bdc94
+} TestCoDeleteByDrainData;
1bdc94
+
1bdc94
+static void coroutine_fn test_co_delete_by_drain(void *opaque)
1bdc94
+{
1bdc94
+    TestCoDeleteByDrainData *dbdd = opaque;
1bdc94
+    BlockBackend *blk = dbdd->blk;
1bdc94
+    BlockDriverState *bs = blk_bs(blk);
1bdc94
+    BDRVTestTopState *tts = bs->opaque;
1bdc94
+    void *buffer = g_malloc(65536);
1bdc94
+    QEMUIOVector qiov;
1bdc94
+    struct iovec iov = {
1bdc94
+        .iov_base = buffer,
1bdc94
+        .iov_len  = 65536,
1bdc94
+    };
1bdc94
+
1bdc94
+    qemu_iovec_init_external(&qiov, &iov, 1);
1bdc94
+
1bdc94
+    /* Pretend some internal write operation from parent to child.
1bdc94
+     * Important: We have to read from the child, not from the parent!
1bdc94
+     * Draining works by first propagating it all up the tree to the
1bdc94
+     * root and then waiting for drainage from root to the leaves
1bdc94
+     * (protocol nodes).  If we have a request waiting on the root,
1bdc94
+     * everything will be drained before we go back down the tree, but
1bdc94
+     * we do not want that.  We want to be in the middle of draining
1bdc94
+     * when this following requests returns. */
1bdc94
+    bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0);
1bdc94
+
1bdc94
+    g_assert_cmpint(bs->refcnt, ==, 1);
1bdc94
+
1bdc94
+    if (!dbdd->detach_instead_of_delete) {
1bdc94
+        blk_unref(blk);
1bdc94
+    } else {
1bdc94
+        BdrvChild *c, *next_c;
1bdc94
+        QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
1bdc94
+            bdrv_unref_child(bs, c);
1bdc94
+        }
1bdc94
+    }
1bdc94
+
1bdc94
+    dbdd->done = true;
1bdc94
+}
1bdc94
+
1bdc94
+/**
1bdc94
+ * Test what happens when some BDS has some children, you drain one of
1bdc94
+ * them and this results in the BDS being deleted.
1bdc94
+ *
1bdc94
+ * If @detach_instead_of_delete is set, the BDS is not going to be
1bdc94
+ * deleted but will only detach all of its children.
1bdc94
+ */
1bdc94
+static void do_test_delete_by_drain(bool detach_instead_of_delete)
1bdc94
+{
1bdc94
+    BlockBackend *blk;
1bdc94
+    BlockDriverState *bs, *child_bs, *null_bs;
1bdc94
+    BDRVTestTopState *tts;
1bdc94
+    TestCoDeleteByDrainData dbdd;
1bdc94
+    Coroutine *co;
1bdc94
+
1bdc94
+    bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR,
1bdc94
+                              &error_abort);
1bdc94
+    bs->total_sectors = 65536 >> BDRV_SECTOR_BITS;
1bdc94
+    tts = bs->opaque;
1bdc94
+
1bdc94
+    null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
1bdc94
+                        &error_abort);
1bdc94
+    bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort);
1bdc94
+
1bdc94
+    /* This child will be the one to pass to requests through to, and
1bdc94
+     * it will stall until a drain occurs */
1bdc94
+    child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR,
1bdc94
+                                    &error_abort);
1bdc94
+    child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS;
1bdc94
+    /* Takes our reference to child_bs */
1bdc94
+    tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_file,
1bdc94
+                                        &error_abort);
1bdc94
+
1bdc94
+    /* This child is just there to be deleted
1bdc94
+     * (for detach_instead_of_delete == true) */
1bdc94
+    null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
1bdc94
+                        &error_abort);
1bdc94
+    bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort);
1bdc94
+
1bdc94
+    blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
1bdc94
+    blk_insert_bs(blk, bs, &error_abort);
1bdc94
+
1bdc94
+    /* Referenced by blk now */
1bdc94
+    bdrv_unref(bs);
1bdc94
+
1bdc94
+    g_assert_cmpint(bs->refcnt, ==, 1);
1bdc94
+    g_assert_cmpint(child_bs->refcnt, ==, 1);
1bdc94
+    g_assert_cmpint(null_bs->refcnt, ==, 1);
1bdc94
+
1bdc94
+
1bdc94
+    dbdd = (TestCoDeleteByDrainData){
1bdc94
+        .blk = blk,
1bdc94
+        .detach_instead_of_delete = detach_instead_of_delete,
1bdc94
+        .done = false,
1bdc94
+    };
1bdc94
+    co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd);
1bdc94
+    qemu_coroutine_enter(co);
1bdc94
+
1bdc94
+    /* Drain the child while the read operation is still pending.
1bdc94
+     * This should result in the operation finishing and
1bdc94
+     * test_co_delete_by_drain() resuming.  Thus, @bs will be deleted
1bdc94
+     * and the coroutine will exit while this drain operation is still
1bdc94
+     * in progress. */
1bdc94
+    bdrv_ref(child_bs);
1bdc94
+    bdrv_drain(child_bs);
1bdc94
+    bdrv_unref(child_bs);
1bdc94
+
1bdc94
+    while (!dbdd.done) {
1bdc94
+        aio_poll(qemu_get_aio_context(), true);
1bdc94
+    }
1bdc94
+
1bdc94
+    if (detach_instead_of_delete) {
1bdc94
+        /* Here, the reference has not passed over to the coroutine,
1bdc94
+         * so we have to delete the BB ourselves */
1bdc94
+        blk_unref(blk);
1bdc94
+    }
1bdc94
+}
1bdc94
+
1bdc94
+
1bdc94
+static void test_delete_by_drain(void)
1bdc94
+{
1bdc94
+    do_test_delete_by_drain(false);
1bdc94
+}
1bdc94
+
1bdc94
+static void test_detach_by_drain(void)
1bdc94
+{
1bdc94
+    do_test_delete_by_drain(true);
1bdc94
+}
1bdc94
+
1bdc94
+
1bdc94
 int main(int argc, char **argv)
1bdc94
 {
1bdc94
     int ret;
1bdc94
@@ -839,6 +1005,9 @@ int main(int argc, char **argv)
1bdc94
     g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
1bdc94
                     test_blockjob_drain_subtree);
1bdc94
 
1bdc94
+    g_test_add_func("/bdrv-drain/deletion", test_delete_by_drain);
1bdc94
+    g_test_add_func("/bdrv-drain/detach", test_detach_by_drain);
1bdc94
+
1bdc94
     ret = g_test_run();
1bdc94
     qemu_event_destroy(&done_event);
1bdc94
     return ret;
1bdc94
-- 
1bdc94
1.8.3.1
1bdc94