Usage: -drive file=xxx,id=Y, \ -drive file=xxxx,id=X,backing_reference.drive_id=Y,backing_reference.hidden-disk.*
It will create such backing chain: {virtio-blk dev 'Y'} {virtio-blk dev 'X'} | | | | v v [base] <- [mid] <- ( Y ) <----------------- (hidden target) <--------------- ( X ) v ^ v ^ v ^ v ^ >>>> drive-backup sync=none >>>> X's backing file is hidden-disk, and hidden-disk's backing file is Y. Disk Y may be opened or reopened in read-write mode, so A block backup job is automatically created: source is Y and target is hidden-disk. Signed-off-by: Wen Congyang <we...@cn.fujitsu.com> Signed-off-by: zhanghailiang <zhang.zhanghaili...@huawei.com> Signed-off-by: Gonglei <arei.gong...@huawei.com> --- block.c | 145 ++++++++++++++++++++++++++++++++++++++++++++- include/block/block.h | 1 + include/block/block_int.h | 1 + tests/qemu-iotests/051 | 13 ++++ tests/qemu-iotests/051.out | 13 ++++ 5 files changed, 170 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index b4d629e..bd7fa9c 100644 --- a/block.c +++ b/block.c @@ -1351,6 +1351,113 @@ free_exit: return ret; } +static void backing_reference_completed(void *opaque, int ret) +{ + BlockDriverState *hidden_disk = opaque; + + assert(!hidden_disk->backing_reference); +} + +static int bdrv_open_backing_reference_file(BlockDriverState *bs, + QDict *options, Error **errp) +{ + const char *backing_name; + QDict *hidden_disk_options = NULL; + BlockDriverState *backing_hd, *hidden_disk; + BlockBackend *backing_blk; + Error *local_err = NULL; + int ret = 0; + + backing_name = qdict_get_try_str(options, "drive_id"); + if (!backing_name) { + error_setg(errp, "Backing reference needs option drive_id"); + ret = -EINVAL; + goto free_exit; + } + qdict_del(options, "drive_id"); + + qdict_extract_subqdict(options, &hidden_disk_options, "hidden-disk."); + if (!qdict_size(hidden_disk_options)) { + error_setg(errp, "Backing reference needs option hidden-disk.*"); + ret = -EINVAL; + goto free_exit; + } + + if (qdict_size(options)) { + const QDictEntry *entry = qdict_first(options); + error_setg(errp, "Backing reference used by '%s' doesn't support " + "the option '%s'", bdrv_get_device_name(bs), entry->key); + ret = -EINVAL; + goto free_exit; + } + + backing_blk = blk_by_name(backing_name); + if (!backing_blk) { + error_set(errp, QERR_DEVICE_NOT_FOUND, backing_name); + ret = -ENOENT; + goto free_exit; + } + + backing_hd = blk_bs(backing_blk); + /* Backing reference itself? */ + if (backing_hd == bs || bdrv_find_overlay(backing_hd, bs)) { + error_setg(errp, "Backing reference itself"); + ret = -EINVAL; + goto free_exit; + } + + if (bdrv_op_is_blocked(backing_hd, BLOCK_OP_TYPE_BACKING_REFERENCE, + errp)) { + ret = -EBUSY; + goto free_exit; + } + + /* hidden-disk is bs's backing file */ + ret = bdrv_open_backing_file(bs, hidden_disk_options, errp); + hidden_disk_options = NULL; + if (ret < 0) { + goto free_exit; + } + + hidden_disk = bs->backing_hd; + if (!hidden_disk->drv || !hidden_disk->drv->supports_backing) { + ret = -EINVAL; + error_setg(errp, "Hidden disk's driver doesn't support backing files"); + goto free_exit; + } + + bdrv_set_backing_hd(hidden_disk, backing_hd); + bdrv_ref(backing_hd); + + /* + * backing hd may be opened or reopened in read-write mode, so we + * should backup backing hd to hidden disk + */ + bdrv_op_unblock(hidden_disk, BLOCK_OP_TYPE_BACKUP_TARGET, + bs->backing_blocker); + bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE, + hidden_disk->backing_blocker); + + bdrv_ref(hidden_disk); + backup_start(backing_hd, hidden_disk, 0, MIRROR_SYNC_MODE_NONE, + BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, + backing_reference_completed, hidden_disk, &local_err); + if (local_err) { + error_propagate(errp, local_err); + bdrv_unref(hidden_disk); + /* FIXME, use which errno? */ + ret = -EIO; + goto free_exit; + } + + bs->backing_reference = true; + +free_exit: + QDECREF(hidden_disk_options); + QDECREF(options); + return ret; +} + /* * Opens a disk image whose options are given as BlockdevRef in another block * device's options. @@ -1604,13 +1711,37 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0) { - QDict *backing_options; + QDict *backing_options, *backing_reference_options; + qdict_extract_subqdict(options, &backing_reference_options, + "backing_reference."); qdict_extract_subqdict(options, &backing_options, "backing."); - ret = bdrv_open_backing_file(bs, backing_options, &local_err); - if (ret < 0) { + + if (qdict_size(backing_reference_options) && + qdict_size(backing_options)) { + error_setg(&local_err, + "Option \"backing_reference.*\" and \"backing.*\"" + " cannot be used together"); + ret = -EINVAL; + QDECREF(backing_reference_options); + QDECREF(backing_options); goto close_and_fail; } + if (qdict_size(backing_reference_options)) { + QDECREF(backing_options); + ret = bdrv_open_backing_reference_file(bs, + backing_reference_options, + &local_err); + if (ret) { + goto close_and_fail; + } + } else { + QDECREF(backing_reference_options); + ret = bdrv_open_backing_file(bs, backing_options, &local_err); + if (ret < 0) { + goto close_and_fail; + } + } } bdrv_refresh_filename(bs); @@ -1941,6 +2072,14 @@ void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->backing_hd) { BlockDriverState *backing_hd = bs->backing_hd; + if (bs->backing_reference) { + assert(backing_hd->backing_hd); + if (backing_hd->backing_hd->job) { + block_job_cancel(backing_hd->backing_hd->job); + } + bdrv_set_backing_hd(backing_hd, NULL); + bdrv_unref(backing_hd->backing_hd); + } bdrv_set_backing_hd(bs, NULL); bdrv_unref(backing_hd); } diff --git a/include/block/block.h b/include/block/block.h index 68f3b1a..7138e90 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -159,6 +159,7 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_RESIZE, BLOCK_OP_TYPE_STREAM, BLOCK_OP_TYPE_REPLACE, + BLOCK_OP_TYPE_BACKING_REFERENCE, BLOCK_OP_TYPE_MAX, } BlockOpType; diff --git a/include/block/block_int.h b/include/block/block_int.h index 08dd8ba..624945d 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -375,6 +375,7 @@ struct BlockDriverState { QDict *full_open_options; char exact_filename[PATH_MAX]; + bool backing_reference; BlockDriverState *backing_hd; BlockDriverState *file; diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 0360f37..fd67f40 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -116,6 +116,19 @@ run_qemu -drive file="$TEST_IMG",file.backing.driver=file,file.backing.filename= run_qemu -drive file="$TEST_IMG",file.backing.driver=qcow2,file.backing.file.filename="$TEST_IMG.orig" echo +echo === Backing file reference === +echo + +run_qemu -drive file="$TEST_IMG",if=none,id=drive0 \ + -drive file="$TEST_IMG",driver=qcow2,backing_reference.drive_id=drive0,backing_reference.hidden-disk.filename="$TEST_IMG.hidden" + +run_qemu -drive file="$TEST_IMG",if=none,id=drive0 \ + -drive file="$TEST_IMG",driver=qcow2,backing_reference.drive_id=drive0,backing_reference.hidden-disk.filename="$TEST_IMG.hidden",backing.file.filename="$TEST_IMG.orig" + +run_qemu -drive file="$TEST_IMG",if=none,id=drive0 \ + -drive file="$TEST_IMG",driver=qcow2,file.backing_reference.drive_id=drive0,file.backing_reference.hidden-disk.filename="$TEST_IMG.hidden",file.backing.file.filename="$TEST_IMG.orig" + +echo echo === Enable and disable lazy refcounting on the command line, plus some invalid values === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 2890eac..cb8340b 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -75,6 +75,19 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.fil QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.backing.driver=qcow2,file.backing.file.filename=TEST_DIR/t.qcow2.orig: Driver doesn't support backing files +=== Backing file reference === + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing_reference.drive_id=drive0,backing_reference.hidden-disk.filename=TEST_DIR/t.qcow2.hidden +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing_reference.drive_id=drive0,backing_reference.hidden-disk.filename=TEST_DIR/t.qcow2.hidden,backing.file.filename=TEST_DIR/t.qcow2.orig +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing=drive0,backing.file.filename=TEST_DIR/t.qcow2.orig: Option "backing_reference.*" and "backing.*" cannot be used together + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0 -drive file=TEST_DIR/t.qcow2,driver=qcow2,file.backing_reference.drive_id=drive0,file.backing_reference.hidden-disk.filename=TEST_DIR/t.qcow2.hidden,file.backing.file.filename=TEST_DIR/t.qcow2.orig +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,file.backing=drive0,file.backing.file.filename=TEST_DIR/t.qcow2.orig: Option "backing_reference.*" and "backing.*" cannot be used together + + === Enable and disable lazy refcounting on the command line, plus some invalid values === Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on -- 2.1.0