qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Hanna Reitz <hreitz@redhat.com>
To: Kevin Wolf <kwolf@redhat.com>
Cc: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>,
	qemu-devel@nongnu.org, qemu-block@nongnu.org
Subject: Re: [PATCH 6/7] block: Let replace_child_noperm free children
Date: Mon, 8 Nov 2021 11:12:52 +0100	[thread overview]
Message-ID: <ce386d54-f183-082e-4ef6-e665ff0ae927@redhat.com> (raw)
In-Reply-To: <YYVQvkRFekBcJL1R@redhat.com>

On 05.11.21 16:41, Kevin Wolf wrote:
> Am 04.11.2021 um 11:38 hat Hanna Reitz geschrieben:
>> In most of the block layer, especially when traversing down from other
>> BlockDriverStates, we assume that BdrvChild.bs can never be NULL.  When
>> it becomes NULL, it is expected that the corresponding BdrvChild pointer
>> also becomes NULL and the BdrvChild object is freed.
>>
>> Therefore, once bdrv_replace_child_noperm() sets the BdrvChild.bs
>> pointer to NULL, it should also immediately set the corresponding
>> BdrvChild pointer (like bs->file or bs->backing) to NULL.
>>
>> In that context, it also makes sense for this function to free the
>> child.  Sometimes we cannot do so, though, because it is called in a
>> transactional context where the caller might still want to reinstate the
>> child in the abort branch (and free it only on commit), so this behavior
>> has to remain optional.
>>
>> In bdrv_replace_child_tran()'s abort handler, we now rely on the fact
>> that the BdrvChild passed to bdrv_replace_child_tran() must have had a
>> non-NULL .bs pointer initially.  Make a note of that and assert it.
>>
>> Signed-off-by: Hanna Reitz <hreitz@redhat.com>
>> ---
>>   block.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++---------
>>   1 file changed, 86 insertions(+), 16 deletions(-)
>>
>> diff --git a/block.c b/block.c
>> index ff45447686..0a85ede8dc 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -87,8 +87,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
>>   static bool bdrv_recurse_has_child(BlockDriverState *bs,
>>                                      BlockDriverState *child);
>>   
>> +static void bdrv_child_free(BdrvChild *child);
>>   static void bdrv_replace_child_noperm(BdrvChild **child,
>> -                                      BlockDriverState *new_bs);
>> +                                      BlockDriverState *new_bs,
>> +                                      bool free_empty_child);
>>   static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
>>                                                 BdrvChild *child,
>>                                                 Transaction *tran);
>> @@ -2256,12 +2258,16 @@ typedef struct BdrvReplaceChildState {
>>       BdrvChild *child;
>>       BdrvChild **childp;
>>       BlockDriverState *old_bs;
>> +    bool free_empty_child;
>>   } BdrvReplaceChildState;
>>   
>>   static void bdrv_replace_child_commit(void *opaque)
>>   {
>>       BdrvReplaceChildState *s = opaque;
>>   
>> +    if (s->free_empty_child && !s->child->bs) {
>> +        bdrv_child_free(s->child);
>> +    }
>>       bdrv_unref(s->old_bs);
>>   }
>>   
>> @@ -2270,8 +2276,23 @@ static void bdrv_replace_child_abort(void *opaque)
>>       BdrvReplaceChildState *s = opaque;
>>       BlockDriverState *new_bs = s->child->bs;
>>   
>> -    /* old_bs reference is transparently moved from @s to *s->childp */
>> -    bdrv_replace_child_noperm(s->childp, s->old_bs);
>> +    /*
>> +     * old_bs reference is transparently moved from @s to s->child;
>> +     * pass &s->child here instead of s->childp, because *s->childp
>> +     * will have been cleared by bdrv_replace_child_tran()'s
>> +     * bdrv_replace_child_noperm() call if new_bs is NULL, and we must
>> +     * not pass a NULL *s->childp here.
>> +     */
>> +    bdrv_replace_child_noperm(&s->child, s->old_bs, true);
> Passing free_empty_child=true with a non-NULL new_bs looks a bit
> confusing because the child isn't supposed to become empty anyway.

I wasn’t sure what to do.  I decided to make the contract “Caller should 
pass false only if they will deal with freeing the child”, and so I 
ended up passing `true` in cases such as here.  I felt like `true` 
should kind of be the default case, and `false` the exception.

>> +    /*
>> +     * The child was pre-existing, so s->old_bs must be non-NULL, and
>> +     * s->child thus must not have been freed
>> +     */
>> +    assert(s->child != NULL);
>> +    if (!new_bs) {
>> +        /* As described above, *s->childp was cleared, so restore it */
>> +        *s->childp = s->child;
>> +    }
> If it wasn't cleared, doesn't it still contain s->child, so this could
> just be an unconditional rollback?

I think so, yes.  We’d still have to explain why the rollback is 
required, though, so would dropping the condition really make it 
simpler?  (Of course this also goes in the opposite direction: Is making 
it conditional simpler? :))

I just feel like the comment would be something like “Restore *s->childp 
in case it was cleared as described above”, and then I’d find it a bit 
strange if the “in case” isn’t part of the code...  *shrug*

Hanna



  reply	other threads:[~2021-11-08 10:14 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-04 10:38 [PATCH 0/7] block: Attempt on fixing 030-reported errors Hanna Reitz
2021-11-04 10:38 ` [PATCH 1/7] stream: Traverse graph after modification Hanna Reitz
2021-11-10 12:17   ` Vladimir Sementsov-Ogievskiy
2021-11-04 10:38 ` [PATCH 2/7] block: Manipulate children list in .attach/.detach Hanna Reitz
2021-11-10 12:46   ` Vladimir Sementsov-Ogievskiy
2021-11-10 15:05     ` Hanna Reitz
2021-11-10 12:51   ` Vladimir Sementsov-Ogievskiy
2021-11-10 15:12     ` Hanna Reitz
2021-11-04 10:38 ` [PATCH 3/7] block: Unite remove_empty_child and child_free Hanna Reitz
2021-11-10 12:52   ` Vladimir Sementsov-Ogievskiy
2021-11-04 10:38 ` [PATCH 4/7] block: Drop detached child from ignore list Hanna Reitz
2021-11-10 13:38   ` Vladimir Sementsov-Ogievskiy via
2021-11-10 17:32     ` Hanna Reitz
2021-11-04 10:38 ` [PATCH 5/7] block: Pass BdrvChild ** to replace_child_noperm Hanna Reitz
2021-11-05 15:15   ` Kevin Wolf
2021-11-08  9:58     ` Hanna Reitz
2021-11-04 10:38 ` [PATCH 6/7] block: Let replace_child_noperm free children Hanna Reitz
2021-11-05 15:41   ` Kevin Wolf
2021-11-08 10:12     ` Hanna Reitz [this message]
2021-11-04 10:38 ` [PATCH 7/7] iotests/030: Unthrottle parallel jobs in reverse Hanna Reitz
2021-11-04 11:58 ` [PATCH 0/7] block: Attempt on fixing 030-reported errors Kevin Wolf
2021-11-04 13:34   ` Hanna Reitz
2021-11-05 15:44 ` Kevin Wolf

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=ce386d54-f183-082e-4ef6-e665ff0ae927@redhat.com \
    --to=hreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=vsementsov@virtuozzo.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).