Message ID | 20220118162738.1366281-9-eesposit@redhat.com |
---|---|
State | New |
Headers | show |
Series | Removal of Aiocontext lock through drains: protect bdrv_replace_child_noperm. | expand |
On 1/18/22 17:27, Emanuele Giuseppe Esposito wrote: > 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. A rewrite of the last two paragraphs: --- Of the two callers of bdrv_set_file_or_backing_noperm, bdrv_reopen_parse_file_or_backing is unique in that it does not complete the reopen, it only prepares a transaction. The actual commit (or abort) is done higher in the call chain. Therefore, the call to bdrv_subtree_drained_end_unlocked must be delayed until after the transaction's fate has been determined and its actions have been performed. Do this by recording a TransactionActionDrv to end the drained section for new_child_bs. --- Likewise, the subject can be "block: keep children drained across a reopen transaction"; explain the change and not the technicalities of how you did it. Paolo > Signed-off-by: Emanuele Giuseppe Esposito <eesposit@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; > } > > /*
On Tue, Jan 18, 2022 at 11:27:34AM -0500, Emanuele Giuseppe Esposito wrote: > 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 <eesposit@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, Please don't cast function pointers. If the types don't match please define a wrapper function so the compiler can check the types.
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; } /*
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 <eesposit@redhat.com> --- block.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)