|
|
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 |
|