From patchwork Mon Apr 4 13:43:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 8741151 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C9817C0553 for ; Mon, 4 Apr 2016 13:47:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BC90F2025A for ; Mon, 4 Apr 2016 13:47:29 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4103E201CE for ; Mon, 4 Apr 2016 13:47:28 +0000 (UTC) Received: from localhost ([::1]:58744 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1an4ql-0005hY-N3 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 04 Apr 2016 09:47:27 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:58309) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1an4oh-0001zn-8O for qemu-devel@nongnu.org; Mon, 04 Apr 2016 09:45:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1an4of-0000Ai-Lt for qemu-devel@nongnu.org; Mon, 04 Apr 2016 09:45:19 -0400 Received: from smtp3.mundo-r.com ([212.51.32.191]:15153 helo=smtp4.mundo-r.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1an4of-0008Qw-B1; Mon, 04 Apr 2016 09:45:17 -0400 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A2CUAwCybgJX/5tjdVtdHYMaU32nFQEBAQEBBQGBEQGSdQENgWsHFwqFbAKBMzgUAQEBAQEBAWUnhEIBAQEDAQIkUhA/EigUGxmIKwEJvH4BAQEBBiCFWIJFhmeFewWNTXCJRIVzgnKCOYJqgWhOhwQjhTKPGh4BAUKCBBmBTWkBhmmBPAEBAQ X-IPAS-Result: A2CUAwCybgJX/5tjdVtdHYMaU32nFQEBAQEBBQGBEQGSdQENgWsHFwqFbAKBMzgUAQEBAQEBAWUnhEIBAQEDAQIkUhA/EigUGxmIKwEJvH4BAQEBBiCFWIJFhmeFewWNTXCJRIVzgnKCOYJqgWhOhwQjhTKPGh4BAUKCBBmBTWkBhmmBPAEBAQ X-IronPort-AV: E=Sophos;i="5.24,440,1454972400"; d="scan'208";a="39334339" Received: from fanzine.igalia.com ([91.117.99.155]) by smtp4.mundo-r.com with ESMTP; 04 Apr 2016 15:44:37 +0200 Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim) id 1an4o0-00050x-Vq; Mon, 04 Apr 2016 15:44:37 +0200 Received: from berto by perseus.local with local (Exim 4.87) (envelope-from ) id 1an4nj-0003vT-36; Mon, 04 Apr 2016 16:44:19 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Mon, 4 Apr 2016 16:43:56 +0300 Message-Id: <3b38569c5caeb92ae2fd6b3c6c7272bcce2dcf85.1459776815.git.berto@igalia.com> X-Mailer: git-send-email 2.8.0.rc3 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 212.51.32.191 Cc: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Max Reitz , Stefan Hajnoczi Subject: [Qemu-devel] [PATCH v9 06/11] block: Support streaming to an intermediate layer X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This makes sure that the image we are steaming into is open in read-write mode during the operation. The block job is created on the destination image, but operation blockers are also set on the active layer. We do this in order to prevent other block jobs from running in parallel in the same chain. See here for details on why that is currently not supported: [Qemu-block] RFC: Status of the intermediate block streaming work https://lists.gnu.org/archive/html/qemu-block/2015-12/msg00180.html Finally, this also unblocks the stream operation in backing files. Signed-off-by: Alberto Garcia --- block.c | 4 +++- block/stream.c | 39 ++++++++++++++++++++++++++++++++++++++- blockdev.c | 2 +- include/block/block_int.h | 5 ++++- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 48638c9..f7698a1 100644 --- a/block.c +++ b/block.c @@ -1252,9 +1252,11 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd) backing_hd->drv ? backing_hd->drv->format_name : ""); bdrv_op_block_all(backing_hd, bs->backing_blocker); - /* Otherwise we won't be able to commit due to check in bdrv_commit */ + /* Otherwise we won't be able to commit or stream */ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, bs->backing_blocker); + bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM, + bs->backing_blocker); out: bdrv_refresh_limits(bs, NULL); } diff --git a/block/stream.c b/block/stream.c index 332b9a1..ac02ddf 100644 --- a/block/stream.c +++ b/block/stream.c @@ -35,8 +35,10 @@ typedef struct StreamBlockJob { BlockJob common; RateLimit limit; BlockDriverState *base; + BlockDriverState *active; BlockdevOnError on_error; char *backing_file_str; + int bs_flags; } StreamBlockJob; static int coroutine_fn stream_populate(BlockDriverState *bs, @@ -66,6 +68,11 @@ static void stream_complete(BlockJob *job, void *opaque) StreamCompleteData *data = opaque; BlockDriverState *base = s->base; + if (s->active) { + bdrv_op_unblock_all(s->active, s->common.blocker); + bdrv_unref(s->active); + } + if (!block_job_is_cancelled(&s->common) && data->reached_end && data->ret == 0) { const char *base_id = NULL, *base_fmt = NULL; @@ -79,6 +86,11 @@ static void stream_complete(BlockJob *job, void *opaque) bdrv_set_backing_hd(job->bs, base); } + /* Reopen the image back in read-only mode if necessary */ + if (s->bs_flags != bdrv_get_flags(job->bs)) { + bdrv_reopen(job->bs, s->bs_flags, NULL); + } + g_free(s->backing_file_str); block_job_completed(&s->common, data->ret); g_free(data); @@ -216,13 +228,15 @@ static const BlockJobDriver stream_job_driver = { .set_speed = stream_set_speed, }; -void stream_start(BlockDriverState *bs, BlockDriverState *base, +void stream_start(BlockDriverState *bs, BlockDriverState *active, + BlockDriverState *base, const char *backing_file_str, int64_t speed, BlockdevOnError on_error, BlockCompletionFunc *cb, void *opaque, Error **errp) { StreamBlockJob *s; + int orig_bs_flags; if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && @@ -231,13 +245,36 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, return; } + /* Make sure that the image is opened in read-write mode */ + orig_bs_flags = bdrv_get_flags(bs); + if (!(orig_bs_flags & BDRV_O_RDWR)) { + if (bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, errp) != 0) { + return; + } + } + s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp); if (!s) { return; } + /* If we are streaming to an intermediate image, we need to block + * the active layer. Due to a race condition, having several block + * jobs running in the same chain is broken and we currently don't + * support it. See here for details: + * https://lists.gnu.org/archive/html/qemu-block/2015-12/msg00180.html + */ + if (active) { + bdrv_op_block_all(active, s->common.blocker); + /* Hold a reference to the active layer, otherwise + * bdrv_close_all() will destroy it if we shut down the VM */ + bdrv_ref(active); + } + s->base = base; + s->active = active; s->backing_file_str = g_strdup(backing_file_str); + s->bs_flags = orig_bs_flags; s->on_error = on_error; s->common.co = qemu_coroutine_create(stream_run); diff --git a/blockdev.c b/blockdev.c index d1f5dfb..2e7712e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3038,7 +3038,7 @@ void qmp_block_stream(const char *device, /* backing_file string overrides base bs filename */ base_name = has_backing_file ? backing_file : base_name; - stream_start(bs, base_bs, base_name, has_speed ? speed : 0, + stream_start(bs, NULL, base_bs, base_name, has_speed ? speed : 0, on_error, block_job_cb, bs, &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 10d8759..0a53d5f 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -597,6 +597,8 @@ int is_windows_drive(const char *filename); /** * stream_start: * @bs: Block device to operate on. + * @active: The active layer of the chain where @bs belongs, or %NULL + * if @bs is already the topmost device. * @base: Block device that will become the new base, or %NULL to * flatten the whole backing file chain onto @bs. * @base_id: The file name that will be written to @bs as the new @@ -613,7 +615,8 @@ int is_windows_drive(const char *filename); * streaming job, the backing file of @bs will be changed to * @base_id in the written image and to @base in the live BlockDriverState. */ -void stream_start(BlockDriverState *bs, BlockDriverState *base, +void stream_start(BlockDriverState *bs, BlockDriverState *active, + BlockDriverState *base, const char *base_id, int64_t speed, BlockdevOnError on_error, BlockCompletionFunc *cb, void *opaque, Error **errp);