Blame SOURCES/kvm-block-Don-t-inactivate-children-before-parents.patch

7711c0
From 747fd28a2c5947940c5f17426ba7a91738f1481c Mon Sep 17 00:00:00 2001
7711c0
From: Kevin Wolf <kwolf@redhat.com>
7711c0
Date: Wed, 28 Nov 2018 09:29:46 +0100
7711c0
Subject: [PATCH 15/34] block: Don't inactivate children before parents
7711c0
7711c0
RH-Author: Kevin Wolf <kwolf@redhat.com>
7711c0
Message-id: <20181128092947.24543-2-kwolf@redhat.com>
7711c0
Patchwork-id: 83179
7711c0
O-Subject: [RHEL-7.7/7.6.z qemu-kvm-rhev PATCH 1/2] block: Don't inactivate children before parents
7711c0
Bugzilla: 1633536
7711c0
RH-Acked-by: Max Reitz <mreitz@redhat.com>
7711c0
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
7711c0
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
7711c0
7711c0
bdrv_child_cb_inactivate() asserts that parents are already inactive
7711c0
when children get inactivated. This precondition is necessary because
7711c0
parents could still issue requests in their inactivation code.
7711c0
7711c0
When block nodes are created individually with -blockdev, all of them
7711c0
are monitor owned and will be returned by bdrv_next() in an undefined
7711c0
order (in practice, in the order of their creation, which is usually
7711c0
children before parents), which obviously fails the assertion:
7711c0
7711c0
qemu: block.c:899: bdrv_child_cb_inactivate: Assertion `bs->open_flags & BDRV_O_INACTIVE' failed.
7711c0
7711c0
This patch fixes the ordering by skipping nodes with still active
7711c0
parents in bdrv_inactivate_recurse() because we know that they will be
7711c0
covered by recursion when the last active parent becomes inactive.
7711c0
7711c0
With the correct parents-before-children ordering, we also got rid of
7711c0
the reason why commit aad0b7a0bfb introduced two passes, so we can go
7711c0
back to a single-pass recursion. This is necessary so we can rely on the
7711c0
BDRV_O_INACTIVE flag to skip nodes with active parents (the flag used
7711c0
to be set only in pass 2, so we would always skip non-root nodes in
7711c0
pass 1 because all parents would still be considered active; setting the
7711c0
flag in pass 1 would mean, that we never skip anything in pass 2 because
7711c0
all parents are already considered inactive).
7711c0
7711c0
Because of the change to single pass, this patch is best reviewed with
7711c0
whitespace changes ignored.
7711c0
7711c0
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7711c0
Reviewed-by: Max Reitz <mreitz@redhat.com>
7711c0
(cherry picked from commit 9e37271f50ec2e95f299dc297ac08f9be0096b48)
7711c0
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7711c0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
---
7711c0
 block.c | 84 +++++++++++++++++++++++++++++++++++++++++------------------------
7711c0
 1 file changed, 53 insertions(+), 31 deletions(-)
7711c0
7711c0
diff --git a/block.c b/block.c
7711c0
index 037d2b0..da9b1a6 100644
7711c0
--- a/block.c
7711c0
+++ b/block.c
7711c0
@@ -4469,45 +4469,68 @@ void bdrv_invalidate_cache_all(Error **errp)
7711c0
     }
7711c0
 }
7711c0
 
7711c0
-static int bdrv_inactivate_recurse(BlockDriverState *bs,
7711c0
-                                   bool setting_flag)
7711c0
+static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active)
7711c0
+{
7711c0
+    BdrvChild *parent;
7711c0
+
7711c0
+    QLIST_FOREACH(parent, &bs->parents, next_parent) {
7711c0
+        if (parent->role->parent_is_bds) {
7711c0
+            BlockDriverState *parent_bs = parent->opaque;
7711c0
+            if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) {
7711c0
+                return true;
7711c0
+            }
7711c0
+        }
7711c0
+    }
7711c0
+
7711c0
+    return false;
7711c0
+}
7711c0
+
7711c0
+static int bdrv_inactivate_recurse(BlockDriverState *bs)
7711c0
 {
7711c0
     BdrvChild *child, *parent;
7711c0
+    uint64_t perm, shared_perm;
7711c0
     int ret;
7711c0
 
7711c0
     if (!bs->drv) {
7711c0
         return -ENOMEDIUM;
7711c0
     }
7711c0
 
7711c0
-    if (!setting_flag && bs->drv->bdrv_inactivate) {
7711c0
+    /* Make sure that we don't inactivate a child before its parent.
7711c0
+     * It will be covered by recursion from the yet active parent. */
7711c0
+    if (bdrv_has_bds_parent(bs, true)) {
7711c0
+        return 0;
7711c0
+    }
7711c0
+
7711c0
+    assert(!(bs->open_flags & BDRV_O_INACTIVE));
7711c0
+
7711c0
+    /* Inactivate this node */
7711c0
+    if (bs->drv->bdrv_inactivate) {
7711c0
         ret = bs->drv->bdrv_inactivate(bs);
7711c0
         if (ret < 0) {
7711c0
             return ret;
7711c0
         }
7711c0
     }
7711c0
 
7711c0
-    if (setting_flag && !(bs->open_flags & BDRV_O_INACTIVE)) {
7711c0
-        uint64_t perm, shared_perm;
7711c0
-
7711c0
-        QLIST_FOREACH(parent, &bs->parents, next_parent) {
7711c0
-            if (parent->role->inactivate) {
7711c0
-                ret = parent->role->inactivate(parent);
7711c0
-                if (ret < 0) {
7711c0
-                    return ret;
7711c0
-                }
7711c0
+    QLIST_FOREACH(parent, &bs->parents, next_parent) {
7711c0
+        if (parent->role->inactivate) {
7711c0
+            ret = parent->role->inactivate(parent);
7711c0
+            if (ret < 0) {
7711c0
+                return ret;
7711c0
             }
7711c0
         }
7711c0
+    }
7711c0
 
7711c0
-        bs->open_flags |= BDRV_O_INACTIVE;
7711c0
+    bs->open_flags |= BDRV_O_INACTIVE;
7711c0
+
7711c0
+    /* Update permissions, they may differ for inactive nodes */
7711c0
+    bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
7711c0
+    bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort);
7711c0
+    bdrv_set_perm(bs, perm, shared_perm);
7711c0
 
7711c0
-        /* Update permissions, they may differ for inactive nodes */
7711c0
-        bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
7711c0
-        bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort);
7711c0
-        bdrv_set_perm(bs, perm, shared_perm);
7711c0
-    }
7711c0
 
7711c0
+    /* Recursively inactivate children */
7711c0
     QLIST_FOREACH(child, &bs->children, next) {
7711c0
-        ret = bdrv_inactivate_recurse(child->bs, setting_flag);
7711c0
+        ret = bdrv_inactivate_recurse(child->bs);
7711c0
         if (ret < 0) {
7711c0
             return ret;
7711c0
         }
7711c0
@@ -4525,7 +4548,6 @@ int bdrv_inactivate_all(void)
7711c0
     BlockDriverState *bs = NULL;
7711c0
     BdrvNextIterator it;
7711c0
     int ret = 0;
7711c0
-    int pass;
7711c0
     GSList *aio_ctxs = NULL, *ctx;
7711c0
 
7711c0
     for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
7711c0
@@ -4537,17 +4559,17 @@ int bdrv_inactivate_all(void)
7711c0
         }
7711c0
     }
7711c0
 
7711c0
-    /* We do two passes of inactivation. The first pass calls to drivers'
7711c0
-     * .bdrv_inactivate callbacks recursively so all cache is flushed to disk;
7711c0
-     * the second pass sets the BDRV_O_INACTIVE flag so that no further write
7711c0
-     * is allowed. */
7711c0
-    for (pass = 0; pass < 2; pass++) {
7711c0
-        for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
7711c0
-            ret = bdrv_inactivate_recurse(bs, pass);
7711c0
-            if (ret < 0) {
7711c0
-                bdrv_next_cleanup(&it);
7711c0
-                goto out;
7711c0
-            }
7711c0
+    for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
7711c0
+        /* Nodes with BDS parents are covered by recursion from the last
7711c0
+         * parent that gets inactivated. Don't inactivate them a second
7711c0
+         * time if that has already happened. */
7711c0
+        if (bdrv_has_bds_parent(bs, false)) {
7711c0
+            continue;
7711c0
+        }
7711c0
+        ret = bdrv_inactivate_recurse(bs);
7711c0
+        if (ret < 0) {
7711c0
+            bdrv_next_cleanup(&it);
7711c0
+            goto out;
7711c0
         }
7711c0
     }
7711c0
 
7711c0
-- 
7711c0
1.8.3.1
7711c0