qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: Hanna Reitz <hreitz@redhat.com>
Cc: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>,
	qemu-devel@nongnu.org, qemu-block@nongnu.org
Subject: Re: [PATCH 5/7] block: Pass BdrvChild ** to replace_child_noperm
Date: Fri, 5 Nov 2021 16:15:29 +0100	[thread overview]
Message-ID: <YYVKkd1giB7eZ0k2@redhat.com> (raw)
In-Reply-To: <20211104103849.46855-6-hreitz@redhat.com>

Am 04.11.2021 um 11:38 hat Hanna Reitz geschrieben:
> bdrv_replace_child_noperm() modifies BdrvChild.bs, and can potentially
> set it to NULL.  That is dangerous, because BDS parents generally assume
> that their children's .bs pointer is never NULL.  We therefore want to
> let bdrv_replace_child_noperm() set the corresponding BdrvChild pointer
> to NULL, too.
> 
> This patch lays the foundation for it by passing a BdrvChild ** pointer
> to bdrv_replace_child_noperm() so that it can later use it to NULL the
> BdrvChild pointer immediately after setting BdrvChild.bs to NULL.
> 
> Signed-off-by: Hanna Reitz <hreitz@redhat.com>
> ---
>  block.c | 59 ++++++++++++++++++++++++++++++++-------------------------
>  1 file changed, 33 insertions(+), 26 deletions(-)
> 
> diff --git a/block.c b/block.c
> index 6d230ad3d1..ff45447686 100644
> --- a/block.c
> +++ b/block.c
> @@ -87,7 +87,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
>  static bool bdrv_recurse_has_child(BlockDriverState *bs,
>                                     BlockDriverState *child);
>  
> -static void bdrv_replace_child_noperm(BdrvChild *child,
> +static void bdrv_replace_child_noperm(BdrvChild **child,
>                                        BlockDriverState *new_bs);
>  static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
>                                                BdrvChild *child,
> @@ -2254,6 +2254,7 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
>  
>  typedef struct BdrvReplaceChildState {
>      BdrvChild *child;
> +    BdrvChild **childp;

Would it be clearer to rename child to old_child now that it can be
changed?

I would also consider having childp first because this is really the
object that we're modifying and potentially rolling back now.

>      BlockDriverState *old_bs;
>  } BdrvReplaceChildState;
>  
> @@ -2269,8 +2270,8 @@ 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->child */
> -    bdrv_replace_child_noperm(s->child, s->old_bs);
> +    /* old_bs reference is transparently moved from @s to *s->childp */
> +    bdrv_replace_child_noperm(s->childp, s->old_bs);
>      bdrv_unref(new_bs);
>  }
>  
> @@ -2287,21 +2288,23 @@ static TransactionActionDrv bdrv_replace_child_drv = {
>   *
>   * The function doesn't update permissions, caller is responsible for this.
>   */
> -static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
> +static void bdrv_replace_child_tran(BdrvChild **childp,
> +                                    BlockDriverState *new_bs,
>                                      Transaction *tran)
>  {
>      BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
>      *s = (BdrvReplaceChildState) {
> -        .child = child,
> -        .old_bs = child->bs,
> +        .child = *childp,
> +        .childp = childp,
> +        .old_bs = (*childp)->bs,
>      };
>      tran_add(tran, &bdrv_replace_child_drv, s);
>  
>      if (new_bs) {
>          bdrv_ref(new_bs);
>      }
> -    bdrv_replace_child_noperm(child, new_bs);
> -    /* old_bs reference is transparently moved from @child to @s */
> +    bdrv_replace_child_noperm(childp, new_bs);
> +    /* old_bs reference is transparently moved from *childp to @s */
>  }
>  
>  /*
> @@ -2672,9 +2675,10 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
>      return permissions[qapi_perm];
>  }
>  
> -static void bdrv_replace_child_noperm(BdrvChild *child,
> +static void bdrv_replace_child_noperm(BdrvChild **childp,
>                                        BlockDriverState *new_bs)
>  {
> +    BdrvChild *child = *childp;
>      BlockDriverState *old_bs = child->bs;
>      int new_bs_quiesce_counter;
>      int drain_saldo;
> @@ -2767,7 +2771,7 @@ static void bdrv_attach_child_common_abort(void *opaque)
>      BdrvChild *child = *s->child;
>      BlockDriverState *bs = child->bs;
>  
> -    bdrv_replace_child_noperm(child, NULL);
> +    bdrv_replace_child_noperm(s->child, NULL);
>  
>      if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
>          bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
> @@ -2867,7 +2871,7 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
>      }
>  
>      bdrv_ref(child_bs);
> -    bdrv_replace_child_noperm(new_child, child_bs);
> +    bdrv_replace_child_noperm(&new_child, child_bs);
>  
>      *child = new_child;
>  
> @@ -2927,12 +2931,12 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
>      return 0;
>  }
>  
> -static void bdrv_detach_child(BdrvChild *child)
> +static void bdrv_detach_child(BdrvChild **childp)
>  {
> -    BlockDriverState *old_bs = child->bs;
> +    BlockDriverState *old_bs = (*childp)->bs;
>  
> -    bdrv_replace_child_noperm(child, NULL);
> -    bdrv_child_free(child);
> +    bdrv_replace_child_noperm(childp, NULL);
> +    bdrv_child_free(*childp);
>  
>      if (old_bs) {
>          /*
> @@ -3038,7 +3042,7 @@ void bdrv_root_unref_child(BdrvChild *child)
>      BlockDriverState *child_bs;
>  
>      child_bs = child->bs;
> -    bdrv_detach_child(child);
> +    bdrv_detach_child(&child);
>      bdrv_unref(child_bs);
>  }
>  
> @@ -4891,30 +4895,33 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
>                                                BdrvChild *child,
>                                                Transaction *tran)
>  {
> +    BdrvChild **childp;
>      BdrvRemoveFilterOrCowChild *s;
>  
> -    assert(child == bs->backing || child == bs->file);
> +    if (child == bs->backing) {
> +        childp = &bs->backing;
> +    } else if (child == bs->file) {
> +        childp = &bs->file;
> +    } else {
> +        g_assert_not_reached();
> +    }
>  
>      if (!child) {
>          return;
>      }
>  
>      if (child->bs) {
> -        bdrv_replace_child_tran(child, NULL, tran);
> +        bdrv_replace_child_tran(childp, NULL, tran);
>      }
>  
>      s = g_new(BdrvRemoveFilterOrCowChild, 1);
>      *s = (BdrvRemoveFilterOrCowChild) {
>          .child = child,
> -        .is_backing = (child == bs->backing),
> +        .is_backing = (childp == &bs->backing),
>      };
>      tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
>  
> -    if (s->is_backing) {
> -        bs->backing = NULL;
> -    } else {
> -        bs->file = NULL;
> -    }
> +    *childp = NULL;
>  }

Hmm... This looks a bit dangerous. Is it guaranteed that bs lives until
the end of the transaction? I guess it has to because we want to be able
to roll back, so probably this is okay, though I'm not sure if I would
bet money on it.

But...

> @@ -4950,7 +4957,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
>                         c->name, from->node_name);
>              return -EPERM;
>          }
> -        bdrv_replace_child_tran(c, to, tran);
> +        bdrv_replace_child_tran(&c, to, tran);
>      }

...here, c is a local stack variable that is even reused in a loop.
bdrv_replace_child_tran() now keeps a pointer to the same variable in
the transaction state for each BdrvChild in the whole parent list, and
by the time the transaction is finalised, I think they are all dangling
pointers anyway because they pointed to stack variables in a function
that has already returned.

bdrv_replace_child_tran() should probably have a comment like we already
have in bdrv_attach_child_common():

 * @child is saved to a new entry of @tran, so that *@child could be reverted to
 * NULL on abort(). So referenced variable must live at least until transaction
 * end.

>      return 0;
> @@ -5099,7 +5106,7 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
>      bdrv_drained_begin(old_bs);
>      bdrv_drained_begin(new_bs);
>  
> -    bdrv_replace_child_tran(child, new_bs, tran);
> +    bdrv_replace_child_tran(&child, new_bs, tran);
>  
>      found = g_hash_table_new(NULL, NULL);
>      refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);

Kevin



  reply	other threads:[~2021-11-05 15:17 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 [this message]
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
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=YYVKkd1giB7eZ0k2@redhat.com \
    --to=kwolf@redhat.com \
    --cc=hreitz@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).