On 18.11.21 10:55, Emanuele Giuseppe Esposito wrote:
On 12/11/2021 15:40, Hanna Reitz wrote:
On 25.10.21 12:17, Emanuele Giuseppe Esposito wrote:
We want to be sure that the functions that write the child and
parent list of a bs are under BQL and drain.
BQL prevents from concurrent writings from the GS API, while
drains protect from I/O.
TODO: drains are missing in some functions using this assert.
Therefore a proper assertion will fail. Because adding drains
requires additional discussions, they will be added in future
series.
Signed-off-by: Emanuele Giuseppe Esposito <eespo...@redhat.com>
Reviewed-by: Paolo Bonzini <pbonz...@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefa...@redhat.com>
---
block.c | 5 +++++
block/io.c | 11 +++++++++++
include/block/block_int-global-state.h | 10 +++++++++-
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/block.c b/block.c
index 41c5883c5c..94bff5c757 100644
--- a/block.c
+++ b/block.c
@@ -2734,12 +2734,14 @@ static void
bdrv_replace_child_noperm(BdrvChild *child,
if (child->klass->detach) {
child->klass->detach(child);
}
+ assert_bdrv_graph_writable(old_bs);
QLIST_REMOVE(child, next_parent);
I think this belongs above the .detach() call (and the QLIST_REMOVE()
belongs into the .detach() implementation, as done in
https://lists.nongnu.org/archive/html/qemu-block/2021-11/msg00240.html,
which has been merged to Kevin’s block branch).
Yes, I rebased on kwolf/block branch. Thank you for pointing that out.
}
child->bs = new_bs;
if (new_bs) {
+ assert_bdrv_graph_writable(new_bs);
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
In both these places it’s a bit strange that the assertion is done on
the child nodes. The subgraph starting from them isn’t modified
after all, so their subgraph technically doesn’t need to be
writable. I think a single assertion on the parent node would be
preferable.
I presume the problem with that is that we don’t have the parent node
here? Do we need a new BdrvChildClass method that performs this
assertion on the parent node?
Uhm I am not sure what you mean here.
Just to recap on how I see this: the assertion
assert_bdrv_graph_writable(bs) is basically used to make sure we are
protecting the write on some fields (childrens and parents lists in
this patch) of a given @bs.
Oh, OK. I understood it to mean that the subgraph starting at `bs` is
mutable, i.e. including all of its recursive children.
And yes, you’re right, the child BDSs are indeed modified, too, so we
should check them, too.
It should work like a rwlock: reading is allowed to be concurrent, but
a write should stop all readers to prevent concurrency issues. This is
achieved by draining.
Draining works on a subgraph, so I suppose it does mean that the whole
subgraph will be mutable. But no matter, at least new_bs is not in the
parent’s subgraph, so it wouldn’t be included in the check if we only
checked the parent.
Let's use the first case that you point out, old_bs (it's specular for
new_bs):
>> + assert_bdrv_graph_writable(old_bs);
>> QLIST_REMOVE(child, next_parent);
So old_bs should be the child "son" (child->bs), meaning
old_bs->parents contains the child. Therefore when a child is removed
by old_bs, we need to be sure we are doing it safely.
So we should check that if old_bs exists, old_bs should be drained, to
prevent any other iothread from reading the ->parents list that is
being updated.
The only thing to keep in mind in this case is that just wrapping a
drain around that won't be enough, because then the child won't be
included in the drain_end(old_bs). Therefore the right way to cover
this drain-wise once the assertion also checks for drains is:
drain_begin(old_bs)
assert_bdrv_graph_writable(old_bs)
QLIST_REMOVE(child, next_parent)
/* old_bs will be under drain_end, but not the child */
bdrv_parent_drained_end_single(child);
bdrv_drained_end(old_bs);
I think you agree on this so far.
Now I think your concern is related to the child "parent", namely
child->opaque. The problem is that in the .detach and .attach
callbacks we are firstly adding/removing the child from the list, and
then calling drain on the subtree.
It was my impression that you’d want bdrv_replace_child_noperm() to
always be called in a section where the subgraph starting from the
parent BDS is drained, not just the child BDSs that are swapped (old_bs
and new_bs).
My abstract concern is that bdrv_replace_child_noperm() does not modify
only old_bs and new_bs, but actually modifies the whole subgraph
starting at the parent node. A concrete example for this is that we
modify not only the children’s parent lists, but also the parent’s
children list.
That’s why I’m asking why we only check that the graph is writable for
the children, when actually I feel like we’re modifying the parent, too.
We would ideally need to do the opposite:
assert_bdrv_graph_writable(bs);
QLIST_REMOVE(child, next);
bdrv_unapply_subtree_drain(child, bs);
In this case I think this would actually work, because removing/adding
the child from the ->children list beforehand just prevents an
additional recursion call (I think, and the fact that tests are
passing seems to confirm my theory).
Of course you know this stuff better than me, so let me know if
something here is wrong.
Well. I’m mostly wondering why you’re discussing how to do the drain
right when I was mostly curious about why we check the children and not
the parent for whether the graph is mutable at their respective
position. O:)
It was my impression that so far we mostly wrapped graph change
operations in drained sections (starting at the parent) and not leave it
to bdrv_replace_child_noperm() to do so. That function only deals with
drain stuff because it balances the drain counters on old_bs and new_bs
to match the counter on the parent’s subgraph.
Hanna