diff --git a/block.c b/block.c index 1271dd2acda7271a4fa8c96525fcfa1e0b94c20c..aa9b5abf5dbccf4318cd75a4ba9c7280bdb3b228 100644 --- a/block.c +++ b/block.c @@ -1097,14 +1097,30 @@ fail: void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) { + if (bs->backing_hd) { + assert(bs->backing_blocker); + bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker); + } else if (backing_hd) { + error_setg(&bs->backing_blocker, + "device is used as backing hd of '%s'", + bs->device_name); + } + bs->backing_hd = backing_hd; if (!backing_hd) { + error_free(bs->backing_blocker); + bs->backing_blocker = NULL; goto out; } bs->open_flags &= ~BDRV_O_NO_BACKING; pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_hd->drv ? backing_hd->drv->format_name : ""); + + bdrv_op_block_all(bs->backing_hd, bs->backing_blocker); + /* Otherwise we won't be able to commit due to check in bdrv_commit */ + bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT, + bs->backing_blocker); out: bdrv_refresh_limits(bs); } @@ -1803,8 +1819,9 @@ void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->backing_hd) { - bdrv_unref(bs->backing_hd); - bs->backing_hd = NULL; + BlockDriverState *backing_hd = bs->backing_hd; + bdrv_set_backing_hd(bs, NULL); + bdrv_unref(backing_hd); } bs->drv->bdrv_close(bs); g_free(bs->opaque); @@ -2006,7 +2023,6 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); assert(bs_new->job == NULL); assert(bs_new->dev == NULL); - assert(bdrv_op_blocker_is_empty(bs_new)); assert(bs_new->io_limits_enabled == false); assert(!throttle_have_timer(&bs_new->throttle_state)); @@ -2025,7 +2041,6 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) /* Check a few fields that should remain attached to the device */ assert(bs_new->dev == NULL); assert(bs_new->job == NULL); - assert(bdrv_op_blocker_is_empty(bs_new)); assert(bs_new->io_limits_enabled == false); assert(!throttle_have_timer(&bs_new->throttle_state)); diff --git a/block/mirror.c b/block/mirror.c index 1c38aa8f7700250fb2fa4201d21b96cdac201b16..94c8661777c4b9e50e96ac6d1deb6c91d5bb00a7 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -498,7 +498,7 @@ immediate_exit: /* drop the bs loop chain formed by the swap: break the loop then * trigger the unref from the top one */ BlockDriverState *p = s->base->backing_hd; - s->base->backing_hd = NULL; + bdrv_set_backing_hd(s->base, NULL); bdrv_unref(p); } } diff --git a/include/block/block_int.h b/include/block/block_int.h index 7b1c013c30d9725fc1506c5ed24198f63c1f1e91..f2e753f63243d43af91ad4c141b578a76f024559 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -369,6 +369,9 @@ struct BlockDriverState { QDict *options; BlockdevDetectZeroesOptions detect_zeroes; + + /* The error object in use for blocking operations on backing_hd */ + Error *backing_blocker; }; int get_tmp_filename(char *filename, int size);