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

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