Implement new API, so that qcow2 supports blockdev-del with force=false. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- include/block/block.h | 2 + block.c | 4 +- block/qcow2.c | 85 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 19 deletions(-)
diff --git a/include/block/block.h b/include/block/block.h index 42d78a7a31..fbb5f3ff6d 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -672,6 +672,8 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs); void bdrv_ref(BlockDriverState *bs); void bdrv_unref(BlockDriverState *bs); void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); +int bdrv_unref_child_safe(BlockDriverState *parent, BdrvChild *child, + Transaction *tran, Error **errp); int bdrv_try_unref(BlockDriverState *bs, Error **errp); BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, diff --git a/block.c b/block.c index 187732c6f8..dd2ddedc86 100644 --- a/block.c +++ b/block.c @@ -3216,8 +3216,8 @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, * When @tran is not NULL, first failure is returned and the action may be * rolled back. */ -static int bdrv_unref_child_safe(BlockDriverState *parent, BdrvChild *child, - Transaction *tran, Error **errp) +int bdrv_unref_child_safe(BlockDriverState *parent, BdrvChild *child, + Transaction *tran, Error **errp) { if (child == NULL) { return 0; diff --git a/block/qcow2.c b/block/qcow2.c index 8ad555feb7..a8f891ee34 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2740,23 +2740,24 @@ static int qcow2_inactivate(BlockDriverState *bs) return qcow2_do_inactivate(bs, true, &error_abort); } -static void qcow2_close(BlockDriverState *bs) -{ - BDRVQcow2State *s = bs->opaque; - qemu_vfree(s->l1_table); - /* else pre-write overlap checks in cache_destroy may crash */ - s->l1_table = NULL; +typedef struct Qcow2CloseState { + BlockDriverState *bs; + void *old_l1_table; +} Qcow2CloseState; - if (!(s->flags & BDRV_O_INACTIVE)) { - qcow2_inactivate(bs); - } +static void qcow2_close_commit(void *opaque) +{ + Qcow2CloseState *cs = opaque; + BlockDriverState *bs = cs->bs; + BDRVQcow2State *s = bs->opaque; + + qemu_vfree(cs->old_l1_table); cache_clean_timer_del(bs); qcow2_cache_destroy(s->l2_table_cache); qcow2_cache_destroy(s->refcount_block_cache); qcrypto_block_free(s->crypto); - s->crypto = NULL; qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); g_free(s->unknown_header_fields); @@ -2766,15 +2767,65 @@ static void qcow2_close(BlockDriverState *bs) g_free(s->image_backing_file); g_free(s->image_backing_format); - if (has_data_file(bs)) { - bdrv_unref_child(bs, s->data_file); - s->data_file = NULL; - } - qcow2_refcount_close(bs); qcow2_free_snapshots(bs); } +static void qcow2_close_abort(void *opaque) +{ + Qcow2CloseState *cs = opaque; + BlockDriverState *bs = cs->bs; + BDRVQcow2State *s = bs->opaque; + + s->l1_table = cs->old_l1_table; +} + +static TransactionActionDrv qcow2_close_drv = { + .commit = qcow2_close_commit, + .abort = qcow2_close_abort, + .clean = g_free, +}; + +static int qcow2_close_safe(BlockDriverState *bs, Transaction *tran, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + Qcow2CloseState *cs = g_new(Qcow2CloseState, 1); + + *cs = (Qcow2CloseState) { + .bs = bs, + .old_l1_table = s->l1_table, + }; + + /* else pre-write overlap checks in cache_destroy may crash */ + s->l1_table = NULL; + if (tran) { + tran_add(tran, &qcow2_close_drv, cs); + } + + if (!(s->flags & BDRV_O_INACTIVE)) { + ret = qcow2_do_inactivate(bs, !tran, errp); + if (ret < 0 && tran) { + return ret; + } + } + + if (has_data_file(bs)) { + ret = bdrv_unref_child_safe(bs, s->data_file, tran, errp); + if (ret < 0 && tran) { + return ret; + } + } + + if (!tran) { + qcow2_close_commit(cs); + g_free(cs); + } + + return 0; +} + static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) { @@ -2793,7 +2844,7 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, crypto = s->crypto; s->crypto = NULL; - qcow2_close(bs); + qcow2_close_safe(bs, NULL, &error_abort); memset(s, 0, sizeof(BDRVQcow2State)); options = qdict_clone_shallow(bs->options); @@ -6043,7 +6094,7 @@ BlockDriver bdrv_qcow2 = { .instance_size = sizeof(BDRVQcow2State), .bdrv_probe = qcow2_probe, .bdrv_open = qcow2_open, - .bdrv_close = qcow2_close, + .bdrv_close_safe = qcow2_close_safe, .bdrv_reopen_prepare = qcow2_reopen_prepare, .bdrv_reopen_commit = qcow2_reopen_commit, .bdrv_reopen_commit_post = qcow2_reopen_commit_post, -- 2.31.1