Signed-off-by: Max Reitz <mre...@redhat.com> --- qapi/block-core.json | 4 ++++ block/stream.c | 23 +++++++++++++++-------- blockdev.c | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/qapi/block-core.json b/qapi/block-core.json index df52a90736..a3c5298cf5 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2518,6 +2518,10 @@ # On successful completion the image file is updated to drop the backing file # and the BLOCK_JOB_COMPLETED event is emitted. # +# In case @device is a filter node, block-stream modifies the first non-filter +# overlay node below it to point to base's backing node (or NULL if @base was +# not specified) instead of modifying @device itself. +# # @job-id: identifier for the newly-created block job. If # omitted, the device name will be used. (Since 2.7) # diff --git a/block/stream.c b/block/stream.c index 1a906fd860..9271e1821a 100644 --- a/block/stream.c +++ b/block/stream.c @@ -63,6 +63,7 @@ static int stream_prepare(Job *job) StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockJob *bjob = &s->common; BlockDriverState *bs = blk_bs(bjob->blk); + BlockDriverState *unfiltered_bs = bdrv_skip_rw_filters(bs); BlockDriverState *base = s->base; Error *local_err = NULL; int ret = 0; @@ -70,7 +71,7 @@ static int stream_prepare(Job *job) bdrv_unfreeze_backing_chain(bs, base); s->chain_frozen = false; - if (bs->backing) { + if (bdrv_filtered_cow_child(unfiltered_bs)) { const char *base_id = NULL, *base_fmt = NULL; if (base) { base_id = s->backing_file_str; @@ -78,8 +79,8 @@ static int stream_prepare(Job *job) base_fmt = base->drv->format_name; } } - ret = bdrv_change_backing_file(bs, base_id, base_fmt); - bdrv_set_backing_hd(bs, base, &local_err); + ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt); + bdrv_set_backing_hd(unfiltered_bs, base, &local_err); if (local_err) { error_report_err(local_err); return -EPERM; @@ -110,7 +111,9 @@ static int coroutine_fn stream_run(Job *job, Error **errp) StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockBackend *blk = s->common.blk; BlockDriverState *bs = blk_bs(blk); + BlockDriverState *unfiltered_bs = bdrv_skip_rw_filters(bs); BlockDriverState *base = s->base; + BlockDriverState *filtered_base; int64_t len; int64_t offset = 0; uint64_t delay_ns = 0; @@ -119,10 +122,12 @@ static int coroutine_fn stream_run(Job *job, Error **errp) int64_t n = 0; /* bytes */ void *buf; - if (!bs->backing) { + if (!bdrv_filtered_cow_child(unfiltered_bs)) { goto out; } + filtered_base = bdrv_filtered_cow_bs(bdrv_find_overlay(bs, base)); + len = bdrv_getlength(bs); if (len < 0) { ret = len; @@ -154,14 +159,14 @@ static int coroutine_fn stream_run(Job *job, Error **errp) copy = false; - ret = bdrv_is_allocated(bs, offset, STREAM_BUFFER_SIZE, &n); + ret = bdrv_is_allocated(unfiltered_bs, offset, STREAM_BUFFER_SIZE, &n); if (ret == 1) { /* Allocated in the top, no need to copy. */ } else if (ret >= 0) { /* Copy if allocated in the intermediate images. Limit to the * known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */ - ret = bdrv_is_allocated_above(backing_bs(bs), base, - offset, n, &n); + ret = bdrv_is_allocated_above(bdrv_filtered_cow_bs(unfiltered_bs), + filtered_base, offset, n, &n); /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { @@ -266,7 +271,9 @@ void stream_start(const char *job_id, BlockDriverState *bs, * disappear from the chain after this operation. The streaming job reads * every block only once, assuming that it doesn't change, so block writes * and resizes. */ - for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) { + for (iter = bdrv_filtered_bs(bs); iter && iter != base; + iter = bdrv_filtered_bs(iter)) + { block_job_add_bdrv(&s->common, "intermediate node", iter, 0, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED, &error_abort); diff --git a/blockdev.c b/blockdev.c index 5036d064d4..a464cabf9e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3235,7 +3235,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, } /* Check for op blockers in the whole chain between bs and base */ - for (iter = bs; iter && iter != base_bs; iter = backing_bs(iter)) { + for (iter = bs; iter && iter != base_bs; iter = bdrv_filtered_bs(iter)) { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } -- 2.21.0