From: Jeff Cody <jcody@redhat.com>
To: "Benoît Canet" <benoit.canet@nodalink.com>
Cc: kwolf@redhat.com, famz@redhat.com, qemu-devel@nongnu.org,
stefanha@redhat.com
Subject: Re: [Qemu-devel] [PATCH v2 2/2] block: Make op blockers recursive
Date: Wed, 1 Oct 2014 11:19:52 -0400 [thread overview]
Message-ID: <20141001151952.GB12347@localhost.localdomain> (raw)
In-Reply-To: <20141001092944.GB5378@nodalink.com>
On Wed, Oct 01, 2014 at 09:29:44AM +0000, Benoît Canet wrote:
>
> Thanks a lot for reviewing this patch.
>
> Since the code is not trivial I will give my arguments for writing it
> this way.
>
Thanks, that is very helpful.
>
> > > +/* block recursively a BDS
> > > + *
> > > + * If base != NULL the caller must make sure that there is no multiple child
> > > + * BDS in the subtree pointed to by bs
> >
> > In the case when base != NULL, should that really matter? In a
> > driver's .bdrv_op_recursive_block/unblock, if that driver has private
> > children (multiple or not), shouldn't the private children be treated
> > as one black box, and blocked / unblocked just like the parent
> > BDS?
>
> This is a stale comment. My bad.
>
> >
> > For instance, what if we had a tree such as this:
> >
> > [quorum1] <---- [active]
> > |
> > | (private)
> > |
> > v
> > [node2]<-- [node1] <--- [imag>
> > if 'quorum1' was to be op blocked, and 'image1' and its children all
> > comprise 'quorum1', wouldn't we always want to lock 'image1' all the
> > way down to 'node2'?
>
> That's what the patch does.
>
OK, great - my comment above was written in response to the stale comment :)
> >
> > Or do we expect that someone will intentionally pass a 'base' pointer
> > along that matches somewhere in the private child tree?
>
> This is not expected by the caller but I wrote the patch so it will also work.
>
> >
> > > + *
> > > + * @bs: the BDS to start to recurse from
> > > + * @base: the BDS where the recursion should end
> > > + * could be NULL if the recursion should go down everywhere
> > > + * @op: the type of op blocker to block
> > > + * @reason: the error message associated with the blocking
> > > + */
> > > +void bdrv_op_block(BlockDriverState *bs, BlockDriverState *base,
> > > + BlockOpType op, Error *reason)
> > > +{
> > > + if (!bs) {
> > > + return;
> > > + }
> > > +
> > > + /* did we recurse down to base ? */
> > > + if (bs == base) {
> > > + return;
> > > + }
> > > +
> > > + /* prevent recursion loop */
> > > + if (bdrv_op_is_blocked_by(bs, op, reason)) {
> > > + return;
> > > + }
> >
> > Should we handle this somehow (isn't this effectively an error, that
> > will prematurely end the blocking of this particular line)?
>
> The main purpose of this is mirror.c and commit.c would form BDS loops on completion.
> These callers could break the look manually but the code would fail
> if a loop is not breaked and the blocker function are called on it.
> So the blocker code have to handle recursion loops.
I think commit/mirror/etc should break any loops prior to calling
recursive functions on those loops (just like it should do before
calling bdrv_unref(), etc..). Otherwise, I think the recursive
functions make assumptions that may be true in certain contexts, but
not necessarily all.
(Hmm, I believe that Fam had a series that did some nice cleanup on
bdrv_drop_intermediate() and related areas that did some loop
breaking, IIRC).
>
> The bdrv_op_is_blocked_by match a reason being the criteria to test the blocker.
>
> So this test will trigger only if a BDS is already blocked by the same reason
> pointer.
>
> This make the bdrv_op_block function idempotent.
>
> note that it is the only sane way I found to make the blockers function handle
> loops.
>
> >
> > If we hit this, we are going to end up in a scenario where we haven't
> > blocked the chain as requested, and we don't know the state of the
> > blocking below this failure point. Seems like the caller should know,
> > and we should have a way of cleaning up.
>
> If we hit this the chain was already blocked with the same reason pointer.
>
> >
> > Actually, now I am wondering if we perhaps shouldn't be building a
> > list to block, and then blocking everything atomically once we know
> > there are no failure points.
> >
>
> I don't think this particular test is a failure point.
>
With the way it is written, the individual BDS is blocked with the
same reason pointer, but not necessarily the whole chain - unless you
make the assumption that blockers will only be used via the recursive
interface, and never individually on a node.
The caller doesn't have an interface to check that the chain is not
blocked - bdrv_op_is_blocked_by() doesn't operate recursively.
If we tried to block a chain that has some portion already blocked for
the same reason, shouldn't that be an error?
> > > +
> > > + /* block first for recursion loop protection to work */
> > > + bdrv_do_op_block(bs, op, reason);
> > > +
> > > + bdrv_op_block(bs->file, base, op, reason);
> > > +
> > > + if (bs->drv && bs->drv->supports_backing) {
> > > + bdrv_op_block(bs->backing_hd, base, op, reason);
> > > + }
> > > +
> > > + if (bs->drv && bs->drv->bdrv_op_recursive_block) {
> > > + bs->drv->bdrv_op_recursive_block(bs, base, op, reason);
> >
> > Do we need to allow .bdrv_op_recursive_block() to fail and return
> > error (and handle it, of course)?
>
> I don't know yet: but lets let this question float a little more in the mail thread.
>
> >
> >
> >
> > s/block/unblock
> Thanks
>
> >
> >
> > > + * @reason: the error message associated with the blocking
> > > + */
> > > +void bdrv_op_unblock(BlockDriverState *bs, BlockDriverState *base,
> > > + BlockOpType op, Error *reason)
> > > +{
> > > + if (!bs) {
> > > + return;
> > > + }
> > > +
> > > + /* did we recurse down to base ? */
> > > + if (bs == base) {
> > > + return;
> > > + }
> > > +
> > > + /* prevent recursion loop */
> > > + if (!bdrv_op_is_blocked_by(bs, op, reason)) {
> > > + return;
> > > + }
> >
> > Do we want to do this negative check here? Even if a given node is
> > not blocked, its children may be, and I would assume (since this is
> > recursive) that if I called bdrv_op_unblock() all of the chain down to
> > 'base' would be unblocked for the given reason.
> >
> > E.g.
> >
> > [image1] <-- [image2] <-- [image3]
> > blocked unblocked blocked
>
> To reach this state the caller code would have to do the following twisted sequence.
>
> block(image3, with_reason1)
> unblock(image2, with_reason1)
> block(image1, with_reason1)
>
> There is no such sequence in the code thanks to the base argument and we could
> enforce that no such sequence ever get written.
>
If we ignore blockdev-add and scenarios where an image node may have
multiple overlays, we might be able to assume that such a sequence
could not exist.
But in that case, should this negative check result in an error?
It would seem at this point we would have encountered one of these
scenarios:
1.) An invalid block/unblock state in the chain, if we assume that no
such sequence should exist
2.) We tried to unblock more than we originally blocked
> >
> > I would assume that bdrv_op_unblock(image2, NULL, reason) would still
> > unblock image1, even if image2 was unblocked.
>
> Should we also assume that bdrv_op_unblock(image4, NULL, reason) with image4 being
> image3 parent unblock everything underneath ?
>
I think we either do that, or return an error. But to have
bdrv_op_unblock() (or bdrv_op_block()) silently stop at some point in
the chain prior to reaching 'base' doesn't seem correct to me.
> > > +static void quorum_op_recursive_block(BlockDriverState *bs,
> > > + BlockDriverState *base,
> > > + BlockOpType op,
> > > + Error *reason)
> > > +{
> > > + BDRVQuorumState *s = bs->opaque;
> > > + int i;
> > > +
> > > + for (i = 0; i < s->num_children; i++) {
> > > + bdrv_op_block(s->bs[i], base, op, reason);
> > > + }
> >
> > I am wondering if the 'base' argument above should always be NULL in
> > the case of private children. I.e., the state of private children
> > trees in their entirety should always reflect the state of the
> > multi-children BDS parent.
>
> Yes we could simply discard it.
>
> > > + /* helps blockers to propagate recursively */
> > > + void (*bdrv_op_recursive_block)(BlockDriverState *bs,
> > > + BlockDriverState *base,
> > > + BlockOpType op,
> > > + Error *reason);
> > > + void (*bdrv_op_recursive_unblock)(BlockDriverState *bs,
> > > + BlockDriverState *base,
> > > + BlockOpType op,
> > > + Error *reason);
> > > +
> >
> > Seems like these new functions would be better named '.bdrv_op_block'
> > and '.bdrv_op_unblock'? That way, recursive or not, it is clear block
> > drivers can implement whatever blocking is appropriate for themselves.
>
> I agree.
>
> Best regards
>
> Benoît
next prev parent reply other threads:[~2014-10-01 15:20 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-22 19:00 [Qemu-devel] [PATCH v2 0/2] Recursive op blockers Benoît Canet
2014-09-22 19:00 ` [Qemu-devel] [PATCH v2 1/2] block: Rename BLOCK_OP_TYPE_REPLACE to BLOCK_OP_TYPE_MIRROR_REPLACE Benoît Canet
2014-09-22 19:00 ` [Qemu-devel] [PATCH v2 2/2] block: Make op blockers recursive Benoît Canet
2014-10-01 4:38 ` Jeff Cody
2014-10-01 4:59 ` Benoît Canet
2014-10-01 9:29 ` Benoît Canet
2014-10-01 15:19 ` Jeff Cody [this message]
2014-10-01 15:42 ` Benoît Canet
2014-09-29 16:08 ` [Qemu-devel] [PATCH v2 0/2] Recursive op blockers Benoît Canet
-- strict thread matches above, loose matches on Subject: below --
2014-09-22 12:40 Benoît Canet
2014-09-22 12:40 ` [Qemu-devel] [PATCH v2 2/2] block: Make op blockers recursive Benoît Canet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20141001151952.GB12347@localhost.localdomain \
--to=jcody@redhat.com \
--cc=benoit.canet@nodalink.com \
--cc=famz@redhat.com \
--cc=kwolf@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.