From patchwork Fri Mar 29 13:29:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Shinkevich X-Patchwork-Id: 10877077 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8F85A14DE for ; Fri, 29 Mar 2019 13:32:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7A9D028EA9 for ; Fri, 29 Mar 2019 13:32:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6DFD028E1A; Fri, 29 Mar 2019 13:32:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id E58AB28E1A for ; Fri, 29 Mar 2019 13:32:15 +0000 (UTC) Received: from localhost ([127.0.0.1]:53018 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rcF-0003mt-4v for patchwork-qemu-devel@patchwork.kernel.org; Fri, 29 Mar 2019 09:32:15 -0400 Received: from eggs.gnu.org ([209.51.188.92]:47530) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rZW-0001rs-3Y for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1h9rZT-0005zP-Vw for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:25 -0400 Received: from relay.sw.ru ([185.231.240.75]:52218) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1h9rZT-0005wt-JL; Fri, 29 Mar 2019 09:29:23 -0400 Received: from [172.16.25.136] (helo=localhost.sw.ru) by relay.sw.ru with esmtp (Exim 4.91) (envelope-from ) id 1h9rZL-0005gf-8Y; Fri, 29 Mar 2019 16:29:15 +0300 From: Andrey Shinkevich To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 29 Mar 2019 16:29:12 +0300 Message-Id: <1553866154-257311-2-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> References: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [PATCH 1/3] block: include base when checking image chain for block allocation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, fam@euphon.net, vsementsov@virtuozzo.com, berto@igalia.com, armbru@redhat.com, mreitz@redhat.com, stefanha@redhat.com, andrey.shinkevich@virtuozzo.com, den@openvz.org, jsnow@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP A caller of the function bdrv_is_allocated_above() may want to include the base node in the search. It is useful when we have parallel commit/stream jobs on the same backing image chain. The base node may be a top one of a parallel job at the same time and go away before the first job completed. Instead of base, pass the node that has the base as its backing one to the new function bdrv_is_allocated_above_inclusive() to manage the issue. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Andrey Shinkevich --- block/io.c | 39 ++++++++++++++++++++++++++++++++++----- include/block/block.h | 5 ++++- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/block/io.c b/block/io.c index dfc153b..8b273e5 100644 --- a/block/io.c +++ b/block/io.c @@ -2317,7 +2317,8 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] * * Return true if (a prefix of) the given range is allocated in any image - * between BASE and TOP (inclusive). BASE can be NULL to check if the given + * between BASE and TOP (TOP included). To check the BASE image, set the + * 'base_included' to 'true'. The BASE can be NULL to check if the given * offset is allocated in any image of the chain. Return false otherwise, * or negative errno on failure. * @@ -2329,16 +2330,21 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, * but 'pnum' will only be 0 when end of file is reached. * */ -int bdrv_is_allocated_above(BlockDriverState *top, - BlockDriverState *base, - int64_t offset, int64_t bytes, int64_t *pnum) +static int bdrv_do_is_allocated_above(BlockDriverState *top, + BlockDriverState *base, + bool base_included, int64_t offset, + int64_t bytes, int64_t *pnum) { BlockDriverState *intermediate; int ret; int64_t n = bytes; + bool exit_loop = top == base ? true : false; + /* Sanity check */ + base_included = base ? base_included : false; + bool include_node = top == base ? base_included : true; intermediate = top; - while (intermediate && intermediate != base) { + while (intermediate && include_node) { int64_t pnum_inter; int64_t size_inter; @@ -2361,12 +2367,35 @@ int bdrv_is_allocated_above(BlockDriverState *top, } intermediate = backing_bs(intermediate); + include_node = intermediate != base; + if (exit_loop) { + include_node = false; + } else if (!include_node && base_included) { + /* Iterate once more */ + include_node = true; + exit_loop = true; + } } *pnum = n; return 0; } +int bdrv_is_allocated_above(BlockDriverState *top, + BlockDriverState *base, + int64_t offset, int64_t bytes, int64_t *pnum) +{ + return bdrv_do_is_allocated_above(top, base, false, offset, bytes, pnum); +} + +int bdrv_is_allocated_above_inclusive(BlockDriverState *top, + BlockDriverState *base, + int64_t offset, int64_t bytes, + int64_t *pnum) +{ + return bdrv_do_is_allocated_above(top, base, true, offset, bytes, pnum); +} + typedef struct BdrvVmstateCo { BlockDriverState *bs; QEMUIOVector *qiov; diff --git a/include/block/block.h b/include/block/block.h index c7a2619..a7846d9 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -448,7 +448,10 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum); int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum); - +int bdrv_is_allocated_above_inclusive(BlockDriverState *top, + BlockDriverState *base, + int64_t offset, int64_t bytes, + int64_t *pnum); bool bdrv_is_read_only(BlockDriverState *bs); int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, bool ignore_allow_rdw, Error **errp); From patchwork Fri Mar 29 13:29:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Shinkevich X-Patchwork-Id: 10877065 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D2FAE14DE for ; Fri, 29 Mar 2019 13:30:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BE3BC28EA2 for ; Fri, 29 Mar 2019 13:30:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BBF6F291BE; Fri, 29 Mar 2019 13:30:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A5DED2912D for ; Fri, 29 Mar 2019 13:30:47 +0000 (UTC) Received: from localhost ([127.0.0.1]:53010 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rap-0002ut-0k for patchwork-qemu-devel@patchwork.kernel.org; Fri, 29 Mar 2019 09:30:47 -0400 Received: from eggs.gnu.org ([209.51.188.92]:47509) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rZU-0001ql-Hw for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1h9rZT-0005yx-L9 for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:24 -0400 Received: from relay.sw.ru ([185.231.240.75]:52226) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1h9rZT-0005wu-2S; Fri, 29 Mar 2019 09:29:23 -0400 Received: from [172.16.25.136] (helo=localhost.sw.ru) by relay.sw.ru with esmtp (Exim 4.91) (envelope-from ) id 1h9rZL-0005gf-Gk; Fri, 29 Mar 2019 16:29:15 +0300 From: Andrey Shinkevich To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 29 Mar 2019 16:29:13 +0300 Message-Id: <1553866154-257311-3-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> References: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [PATCH 2/3] block/stream: refactor stream_run: drop goto X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, fam@euphon.net, vsementsov@virtuozzo.com, berto@igalia.com, armbru@redhat.com, mreitz@redhat.com, stefanha@redhat.com, andrey.shinkevich@virtuozzo.com, den@openvz.org, jsnow@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Andrey Shinkevich --- block/stream.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/block/stream.c b/block/stream.c index 6253c86..c065e99 100644 --- a/block/stream.c +++ b/block/stream.c @@ -122,13 +122,12 @@ static int coroutine_fn stream_run(Job *job, Error **errp) void *buf; if (!bs->backing) { - goto out; + return 0; } len = bdrv_getlength(bs); if (len < 0) { - ret = len; - goto out; + return len; } job_progress_set_remaining(&s->common.job, len); @@ -205,14 +204,10 @@ static int coroutine_fn stream_run(Job *job, Error **errp) bdrv_disable_copy_on_read(bs); } - /* Do not remove the backing file if an error was there but ignored. */ - ret = error; - qemu_vfree(buf); -out: - /* Modify backing chain and close BDSes in main loop */ - return ret; + /* Do not remove the backing file if an error was there but ignored. */ + return error; } static const BlockJobDriver stream_job_driver = { From patchwork Fri Mar 29 13:29:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Shinkevich X-Patchwork-Id: 10877081 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 98402186D for ; Fri, 29 Mar 2019 13:33:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8271628E26 for ; Fri, 29 Mar 2019 13:33:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7605928E45; Fri, 29 Mar 2019 13:33:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id ACBFC28E36 for ; Fri, 29 Mar 2019 13:33:33 +0000 (UTC) Received: from localhost ([127.0.0.1]:53026 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rdU-0004NZ-Pl for patchwork-qemu-devel@patchwork.kernel.org; Fri, 29 Mar 2019 09:33:32 -0400 Received: from eggs.gnu.org ([209.51.188.92]:47542) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h9rZW-0001sI-ND for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1h9rZT-0005z6-Qx for qemu-devel@nongnu.org; Fri, 29 Mar 2019 09:29:26 -0400 Received: from relay.sw.ru ([185.231.240.75]:52232) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1h9rZT-0005y2-2R; Fri, 29 Mar 2019 09:29:23 -0400 Received: from [172.16.25.136] (helo=localhost.sw.ru) by relay.sw.ru with esmtp (Exim 4.91) (envelope-from ) id 1h9rZL-0005gf-NR; Fri, 29 Mar 2019 16:29:15 +0300 From: Andrey Shinkevich To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 29 Mar 2019 16:29:14 +0300 Message-Id: <1553866154-257311-4-git-send-email-andrey.shinkevich@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> References: <1553866154-257311-1-git-send-email-andrey.shinkevich@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [PATCH 3/3] block/stream: introduce a bottom node X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, fam@euphon.net, vsementsov@virtuozzo.com, berto@igalia.com, armbru@redhat.com, mreitz@redhat.com, stefanha@redhat.com, andrey.shinkevich@virtuozzo.com, den@openvz.org, jsnow@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP The bottom node is the intermediate block device that has the base as its backing image. It is used instead of the base node while a block stream job is running to avoid dependency on the base that may change due to the parallel jobs. The change may take place due to a filter node as well that is inserted between the base and the intermediate bottom node. It occurs when the base node is the top one for another commit or stream job. Suggested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Andrey Shinkevich --- block/stream.c | 56 +++++++++++++++++++++++++---------------------- block/trace-events | 2 +- blockdev.c | 10 ++++++++- include/block/block_int.h | 6 ++--- tests/qemu-iotests/245 | 4 ++-- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/block/stream.c b/block/stream.c index c065e99..d4d0737 100644 --- a/block/stream.c +++ b/block/stream.c @@ -31,7 +31,7 @@ enum { typedef struct StreamBlockJob { BlockJob common; - BlockDriverState *base; + BlockDriverState *bottom; BlockdevOnError on_error; char *backing_file_str; bool bs_read_only; @@ -56,7 +56,7 @@ static void stream_abort(Job *job) if (s->chain_frozen) { BlockJob *bjob = &s->common; - bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base); + bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->bottom); } } @@ -65,11 +65,11 @@ 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 *base = s->base; + BlockDriverState *base = backing_bs(s->bottom); Error *local_err = NULL; int ret = 0; - bdrv_unfreeze_backing_chain(bs, base); + bdrv_unfreeze_backing_chain(bs, s->bottom); s->chain_frozen = false; if (bs->backing) { @@ -112,7 +112,7 @@ 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 *base = s->base; + bool enable_cor = !!backing_bs(s->bottom); int64_t len; int64_t offset = 0; uint64_t delay_ns = 0; @@ -125,6 +125,11 @@ static int coroutine_fn stream_run(Job *job, Error **errp) return 0; } + if (bs == s->bottom) { + /* Nothing to stream */ + return 0; + } + len = bdrv_getlength(bs); if (len < 0) { return len; @@ -138,7 +143,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) * backing chain since the copy-on-read operation does not take base into * account. */ - if (!base) { + if (!enable_cor) { bdrv_enable_copy_on_read(bs); } @@ -161,9 +166,8 @@ static int coroutine_fn stream_run(Job *job, Error **errp) } 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_inclusive(backing_bs(bs), s->bottom, + offset, n, &n); /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { n = len - offset; @@ -200,7 +204,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) } } - if (!base) { + if (!enable_cor) { bdrv_disable_copy_on_read(bs); } @@ -225,13 +229,14 @@ static const BlockJobDriver stream_job_driver = { }; void stream_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, const char *backing_file_str, + BlockDriverState *bottom, const char *backing_file_str, int creation_flags, int64_t speed, BlockdevOnError on_error, Error **errp) { StreamBlockJob *s; BlockDriverState *iter; bool bs_read_only; + int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; /* Make sure that the image is opened in read-write mode */ bs_read_only = bdrv_is_read_only(bs); @@ -245,37 +250,36 @@ void stream_start(const char *job_id, BlockDriverState *bs, * already have our own plans. Also don't allow resize as the image size is * queried only at the job start and then cached. */ s = block_job_create(job_id, &stream_job_driver, NULL, bs, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | - BLK_PERM_GRAPH_MOD, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | - BLK_PERM_WRITE, + basic_flags | BLK_PERM_GRAPH_MOD, + basic_flags | BLK_PERM_WRITE, speed, creation_flags, NULL, NULL, errp); if (!s) { goto fail; } - /* Block all intermediate nodes between bs and base, because they will - * 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)) { - block_job_add_bdrv(&s->common, "intermediate node", iter, 0, - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED, - &error_abort); + /* + * Block all intermediate nodes between bs and bottom (inclusive), because + * they will disappear from the chain after this operation. The streaming + * job reads every block only once, assuming that it doesn't change, so + * forbid writes and resizes. + */ + for (iter = bs; iter != bottom; iter = backing_bs(iter)) { + block_job_add_bdrv(&s->common, "intermediate node", backing_bs(iter), + 0, basic_flags, &error_abort); } - if (bdrv_freeze_backing_chain(bs, base, errp) < 0) { + if (bdrv_freeze_backing_chain(bs, bottom, errp) < 0) { job_early_fail(&s->common.job); goto fail; } - s->base = base; + s->bottom = bottom; s->backing_file_str = g_strdup(backing_file_str); s->bs_read_only = bs_read_only; s->chain_frozen = true; s->on_error = on_error; - trace_stream_start(bs, base, s); + trace_stream_start(bs, bottom, s); job_start(&s->common.job); return; diff --git a/block/trace-events b/block/trace-events index e6bb5a8..36e7b79 100644 --- a/block/trace-events +++ b/block/trace-events @@ -20,7 +20,7 @@ bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_of # stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" -stream_start(void *bs, void *base, void *s) "bs %p base %p s %p" +stream_start(void *bs, void *bottom, void *s) "bs %p bottom %p s %p" # commit.c commit_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" diff --git a/blockdev.c b/blockdev.c index 4775a07..bc84312 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3164,6 +3164,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, { BlockDriverState *bs, *iter; BlockDriverState *base_bs = NULL; + BlockDriverState *bottom_node = NULL; AioContext *aio_context; Error *local_err = NULL; const char *base_name = NULL; @@ -3237,7 +3238,14 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, job_flags |= JOB_MANUAL_DISMISS; } - stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, + /* Find the bottom node that has the base as its backing image */ + bottom_node = bs; + while ((iter = backing_bs(bottom_node)) != base_bs) { + bottom_node = iter; + } + assert(bottom_node); + + stream_start(has_job_id ? job_id : NULL, bs, bottom_node, base_name, job_flags, has_speed ? speed : 0, on_error, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/include/block/block_int.h b/include/block/block_int.h index 01e855a..8ab1144 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1019,8 +1019,8 @@ int is_windows_drive(const char *filename); * @job_id: The id of the newly-created job, or %NULL to use the * device name of @bs. * @bs: Block device to operate on. - * @base: Block device that will become the new base, or %NULL to - * flatten the whole backing file chain onto @bs. + * @bottom_node: The intermediate block device right above the new base. + * If base is %NULL, the whole backing file chain is flattened onto @bs. * @backing_file_str: The file name that will be written to @bs as the * the new backing file if the job completes. Ignored if @base is %NULL. * @creation_flags: Flags that control the behavior of the Job lifetime. @@ -1037,7 +1037,7 @@ int is_windows_drive(const char *filename); * BlockDriverState. */ void stream_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, const char *backing_file_str, + BlockDriverState *bottom_node, const char *backing_file_str, int creation_flags, int64_t speed, BlockdevOnError on_error, Error **errp); diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 7891a21..d11e73c 100644 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -859,9 +859,9 @@ class TestBlockdevReopen(iotests.QMPTestCase): device = 'hd0', base_node = 'hd2', speed = 512 * 1024) self.assert_qmp(result, 'return', {}) - # We can't remove hd2 while the stream job is ongoing + # We can remove hd2 while the stream job is ongoing opts['backing']['backing'] = None - self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'") + self.reopen(opts, {}) # We can't remove hd1 while the stream job is ongoing opts['backing'] = None