0727d3
From 6348063b91b2370cc27153fd58fd11a6681631f6 Mon Sep 17 00:00:00 2001
0727d3
From: Hanna Reitz <hreitz@redhat.com>
0727d3
Date: Wed, 16 Feb 2022 11:53:53 +0100
0727d3
Subject: [PATCH 22/24] block: Make bdrv_refresh_limits() non-recursive
0727d3
MIME-Version: 1.0
0727d3
Content-Type: text/plain; charset=UTF-8
0727d3
Content-Transfer-Encoding: 8bit
0727d3
0727d3
RH-Author: Hanna Reitz <hreitz@redhat.com>
0727d3
RH-MergeRequest: 189: block: Make bdrv_refresh_limits() non-recursive
0727d3
RH-Commit: [1/3] 1a1fe37f8d8f0344dd8639d6cc9d884d1aff9096
0727d3
RH-Bugzilla: 2072932
0727d3
RH-Acked-by: Eric Blake <eblake@redhat.com>
0727d3
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
0727d3
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
0727d3
0727d3
bdrv_refresh_limits() recurses down to the node's children.  That does
0727d3
not seem necessary: When we refresh limits on some node, and then
0727d3
recurse down and were to change one of its children's BlockLimits, then
0727d3
that would mean we noticed the changed limits by pure chance.  The fact
0727d3
that we refresh the parent's limits has nothing to do with it, so the
0727d3
reason for the change probably happened before this point in time, and
0727d3
we should have refreshed the limits then.
0727d3
0727d3
Consequently, we should actually propagate block limits changes upwards,
0727d3
not downwards.  That is a separate and pre-existing issue, though, and
0727d3
so will not be addressed in this patch.
0727d3
0727d3
The problem with recursing is that bdrv_refresh_limits() is not atomic.
0727d3
It begins with zeroing BDS.bl, and only then sets proper, valid limits.
0727d3
If we do not drain all nodes whose limits are refreshed, then concurrent
0727d3
I/O requests can encounter invalid request_alignment values and crash
0727d3
qemu.  Therefore, a recursing bdrv_refresh_limits() requires the whole
0727d3
subtree to be drained, which is currently not ensured by most callers.
0727d3
0727d3
A non-recursive bdrv_refresh_limits() only requires the node in question
0727d3
to not receive I/O requests, and this is done by most callers in some
0727d3
way or another:
0727d3
- bdrv_open_driver() deals with a new node with no parents yet
0727d3
- bdrv_set_file_or_backing_noperm() acts on a drained node
0727d3
- bdrv_reopen_commit() acts only on drained nodes
0727d3
- bdrv_append() should in theory require the node to be drained; in
0727d3
  practice most callers just lock the AioContext, which should at least
0727d3
  be enough to prevent concurrent I/O requests from accessing invalid
0727d3
  limits
0727d3
0727d3
So we can resolve the bug by making bdrv_refresh_limits() non-recursive.
0727d3
0727d3
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1879437
0727d3
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
0727d3
Reviewed-by: Eric Blake <eblake@redhat.com>
0727d3
Message-Id: <20220216105355.30729-2-hreitz@redhat.com>
0727d3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
0727d3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
0727d3
(cherry picked from commit 4d378bbd831bdd2f6e6adcd4ea5b77b6effaa627)
0727d3
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
0727d3
---
0727d3
 block/io.c | 4 ----
0727d3
 1 file changed, 4 deletions(-)
0727d3
0727d3
diff --git a/block/io.c b/block/io.c
0727d3
index 4e4cb556c5..c3e7301613 100644
0727d3
--- a/block/io.c
0727d3
+++ b/block/io.c
0727d3
@@ -189,10 +189,6 @@ void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp)
0727d3
     QLIST_FOREACH(c, &bs->children, next) {
0727d3
         if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
0727d3
         {
0727d3
-            bdrv_refresh_limits(c->bs, tran, errp);
0727d3
-            if (*errp) {
0727d3
-                return;
0727d3
-            }
0727d3
             bdrv_merge_limits(&bs->bl, &c->bs->bl);
0727d3
             have_limits = true;
0727d3
         }
0727d3
-- 
0727d3
2.35.3
0727d3