Depending on the options given to reopen_state, bdrv_reopen_parse_file_or_backing could pick another bs that could be from another graph, and thus not protected by subtree_drained_begin called by the callers of this function.
We can't simply drain-undrain here, because of transactions. To simplify the logic, transactions always assume that they are run under drain, so the various subtree_drain introduced so far always take care of covering tran_commit(). And since we cannot directly do it, as the transaction is created/committed higher above, we can just add a new transaction to the list that just executes subtree_drained_end to match the drained_begin done in this function. Signed-off-by: Emanuele Giuseppe Esposito <eespo...@redhat.com> --- block.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/block.c b/block.c index fb5bc3077a..fcc44a49a0 100644 --- a/block.c +++ b/block.c @@ -4522,6 +4522,10 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, return bdrv_reopen(bs, opts, true, errp); } +TransactionActionDrv bdrv_drv_subtree_end = { + .clean = (void (*)(void *)) bdrv_subtree_drained_end_unlocked, +}; + /* * Take a BDRVReopenState and check if the value of 'backing' in the * reopen_state->options QDict is valid or not. @@ -4550,6 +4554,7 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, const char *child_name = is_backing ? "backing" : "file"; QObject *value; const char *str; + int ret = 0; assert(qemu_in_main_thread()); @@ -4573,6 +4578,8 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, "cycle", str, child_name, bs->node_name); return -EINVAL; } + /* This will be paired with a drained_end in tran_commit */ + bdrv_subtree_drained_begin_unlocked(new_child_bs); break; default: /* @@ -4583,18 +4590,19 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, } if (old_child_bs == new_child_bs) { - return 0; + goto end; } if (old_child_bs) { if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) { - return 0; + goto end; } if (old_child_bs->implicit) { error_setg(errp, "Cannot replace implicit %s child of %s", child_name, bs->node_name); - return -EPERM; + ret = -EPERM; + goto end; } } @@ -4605,7 +4613,8 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, */ error_setg(errp, "'%s' is a %s filter node that does not support a " "%s child", bs->node_name, bs->drv->format_name, child_name); - return -EINVAL; + ret = -EINVAL; + goto end; } if (is_backing) { @@ -4614,8 +4623,14 @@ static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, reopen_state->old_file_bs = old_child_bs; } - return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, + ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, tran, errp); + +end: + if (new_child_bs) { + tran_add(tran, &bdrv_drv_subtree_end, new_child_bs); + } + return ret; } /* -- 2.31.1