Signed-off-by: Fam Zheng <f...@redhat.com> --- block.c | 109 +++++++++++++++++++++++++++++----------------- block/mirror.c | 2 +- include/block/block.h | 3 +- include/block/block_int.h | 3 ++ 4 files changed, 76 insertions(+), 41 deletions(-)
diff --git a/block.c b/block.c index 45410ef..6fda85f 100644 --- a/block.c +++ b/block.c @@ -959,63 +959,87 @@ fail: /* * Opens the backing file for a BlockDriverState if not yet open * + * If backing_bs is not NULL or empty, find the BDS by name and reference it as + * the backing_hd, in this case options is ignored. + * * options is a QDict of options to pass to the block drivers, or NULL for an * empty set of options. The reference to the QDict is transferred to this * function (even on failure), so if the caller intends to reuse the dictionary, * it needs to use QINCREF() before calling bdrv_file_open. */ -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) +int bdrv_open_backing_file(BlockDriverState *bs, const char *backing_bs, + QDict *options, Error **errp) { char backing_filename[PATH_MAX]; int back_flags, ret; BlockDriver *back_drv = NULL; Error *local_err = NULL; - if (bs->backing_hd != NULL) { - QDECREF(options); - return 0; - } + if (backing_bs && *backing_bs != '\0') { + bs->backing_hd = bdrv_find(backing_bs); + if (!bs->backing_hd) { + error_setg(errp, "Backing device not found: %s", backing_bs); + return -ENOENT; + } + bdrv_ref(bs->backing_hd); + } else { + if (bs->backing_hd != NULL) { + QDECREF(options); + return 0; + } - /* NULL means an empty set of options */ - if (options == NULL) { - options = qdict_new(); - } + /* NULL means an empty set of options */ + if (options == NULL) { + options = qdict_new(); + } - bs->open_flags &= ~BDRV_O_NO_BACKING; - if (qdict_haskey(options, "file.filename")) { - backing_filename[0] = '\0'; - } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { - QDECREF(options); - return 0; - } else { - bdrv_get_full_backing_filename(bs, backing_filename, - sizeof(backing_filename)); - } + bs->open_flags &= ~BDRV_O_NO_BACKING; + if (qdict_haskey(options, "file.filename")) { + backing_filename[0] = '\0'; + } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { + QDECREF(options); + return 0; + } else { + bdrv_get_full_backing_filename(bs, backing_filename, + sizeof(backing_filename)); + } - bs->backing_hd = bdrv_new(""); + bs->backing_hd = bdrv_new(""); - if (bs->backing_format[0] != '\0') { - back_drv = bdrv_find_format(bs->backing_format); - } + if (bs->backing_format[0] != '\0') { + back_drv = bdrv_find_format(bs->backing_format); + } - /* backing files always opened read-only */ - back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | - BDRV_O_COPY_ON_READ); + /* backing files always opened read-only */ + back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | + BDRV_O_COPY_ON_READ); - ret = bdrv_open(bs->backing_hd, - *backing_filename ? backing_filename : NULL, options, - back_flags, back_drv, &local_err); - if (ret < 0) { - bdrv_unref(bs->backing_hd); - bs->backing_hd = NULL; - bs->open_flags |= BDRV_O_NO_BACKING; - error_setg(errp, "Could not open backing file: %s", - error_get_pretty(local_err)); - error_free(local_err); - return ret; + ret = bdrv_open(bs->backing_hd, + *backing_filename ? backing_filename : NULL, options, + back_flags, back_drv, &local_err); + if (ret < 0) { + bdrv_unref(bs->backing_hd); + bs->backing_hd = NULL; + bs->open_flags |= BDRV_O_NO_BACKING; + error_setg(errp, "Could not open backing file: %s", + error_get_pretty(local_err)); + error_free(local_err); + return ret; + } } + assert(bs->backing_hd); + assert(!bs->backing_blocker); + error_setg(&bs->backing_blocker, + "device is used as backing hd of '%s'", + bs->device_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); pstrcpy(bs->backing_file, sizeof(bs->backing_file), bs->backing_hd->file->filename); + pstrcpy(bs->backing_format, sizeof(bs->backing_format), + bs->backing_hd->drv->format_name); return 0; } @@ -1166,9 +1190,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0) { QDict *backing_options; - qdict_extract_subqdict(options, &backing_options, "backing."); - ret = bdrv_open_backing_file(bs, backing_options, &local_err); + ret = bdrv_open_backing_file(bs, qdict_get_try_str(options, "backing"), + backing_options, &local_err); + + qdict_del(options, "backing"); if (ret < 0) { goto close_and_fail; } @@ -1461,9 +1487,14 @@ void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->backing_hd) { + assert(bs->backing_blocker); + bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker); bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; } + if (bs->backing_blocker) { + error_free(bs->backing_blocker); + } bs->drv->bdrv_close(bs); g_free(bs->opaque); #ifdef _WIN32 diff --git a/block/mirror.c b/block/mirror.c index 7b95acf..ce0103a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -508,7 +508,7 @@ static void mirror_complete(BlockJob *job, Error **errp) Error *local_err = NULL; int ret; - ret = bdrv_open_backing_file(s->target, NULL, &local_err); + ret = bdrv_open_backing_file(s->target, NULL, NULL, &local_err); if (ret < 0) { char backing_filename[PATH_MAX]; bdrv_get_full_backing_filename(s->target, backing_filename, diff --git a/include/block/block.h b/include/block/block.h index 693d305..67da5d5 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -157,7 +157,8 @@ int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_discard_flags(const char *mode, int *flags); int bdrv_file_open(BlockDriverState **pbs, const char *filename, QDict *options, int flags, Error **errp); -int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); +int bdrv_open_backing_file(BlockDriverState *bs, const char *backing_bs, + QDict *options, Error **errp); int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, int flags, BlockDriver *drv, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, diff --git a/include/block/block_int.h b/include/block/block_int.h index d8cef85..ffea9ee 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -317,6 +317,9 @@ struct BlockDriverState { BlockJob *job; QDict *options; + + /* For backing reference, block the operations of named backing device */ + Error *backing_blocker; }; int get_tmp_filename(char *filename, int size); -- 1.8.4.2