qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Benoît Canet" <benoit.canet@nodalink.com>
To: Jeff Cody <jcody@redhat.com>
Cc: kwolf@redhat.com, famz@redhat.com, stefanha@redhat.com,
	qemu-devel@nongnu.org, "Benoît Canet" <benoit.canet@nodalink.com>
Subject: Re: [Qemu-devel] [PATCH v2 2/2] block: Make op blockers recursive
Date: Wed, 1 Oct 2014 09:29:44 +0000	[thread overview]
Message-ID: <20141001092944.GB5378@nodalink.com> (raw)
In-Reply-To: <20141001043842.GA12347@localhost.localdomain>


Thanks a lot for reviewing this patch.

Since the code is not trivial I will give my arguments for writing it
this way.


> > +/* 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.

> 
> 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.

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.

> > +
> > +    /* 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.

> 
> 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 ?

> > +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

  parent reply	other threads:[~2014-10-01  9:29 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 [this message]
2014-10-01 15:19       ` Jeff Cody
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=20141001092944.GB5378@nodalink.com \
    --to=benoit.canet@nodalink.com \
    --cc=famz@redhat.com \
    --cc=jcody@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).