From patchwork Thu Jun 14 15:48:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929531 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="n61tLpkc"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Sx1yzSz9s3q for ; Fri, 15 Jun 2018 01:53:37 +1000 (AEST) Received: from localhost ([::1]:41420 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUZ4-0003U7-L4 for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:53:34 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53126) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000j9-VR for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003nF-1S for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37181) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jk-FW; Thu, 14 Jun 2018 11:49:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=fPiztnIKL24vzWoOe5dv9BKEgatFA+70o+eRQ2Vrhi4=; b=n61tLpkcDKVWbfEErDTaKoH39dFPZTbDy3MCiaXYwu+Vxf7od6C4+Cl0k2aYROtbf/2KkEqqD/pJ/Xh4gr3mdj9p33qA3btWRHEZ2cLFlLzj0FoqR+COmMTU0i0lSP3CD/NsSotmlqVCjbYJzspcf0XuZeVeF7mitsEtK/ajBCySfQDxRhxsFjtdhIwJ84wu6wUMHJSqRUEk3gCKNP9z5ilvw45RZD/RA//+2azEBWKNcpcjtvsiV2Bj9c/G+mT19LRp7NxTQcdsvpXE5QgcXxJhXCBG4W7CdVugJlBg4uczT1x+vNJIinhDy8+vqfejUfY6vIZqy0BjYRdxk2ttiA==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004du-FG; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008NX-4n; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:48:58 +0300 Message-Id: X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 01/10] file-posix: Forbid trying to change unsupported options during reopen 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" The file-posix code is used for the "file", "host_device" and "host_cdrom" drivers, and it allows reopening images. However the only option that is actually processed is "x-check-cache-dropped", and changes in all other options (e.g. "filename") are silently ignored. While we could allow changing some of the other options, let's keep things as they are for now but return an error if the user tries to change any of them. Signed-off-by: Alberto Garcia --- block/file-posix.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 07bb061fe4..1511a4d69d 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -813,8 +813,13 @@ static int raw_reopen_prepare(BDRVReopenState *state, goto out; } - rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", - s->check_cache_dropped); + rs->check_cache_dropped = + qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false); + + /* This driver's reopen function doesn't currently allow changing + * other options, so let's put them back in the original QDict and + * bdrv_reopen_prepare() will detect changes and complain. */ + qemu_opts_to_qdict(opts, state->options); if (s->type == FTYPE_CD) { rs->open_flags |= O_NONBLOCK; From patchwork Thu Jun 14 15:48:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929539 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="WzhPjvDo"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Wg5ZN6z9ry1 for ; Fri, 15 Jun 2018 01:55:59 +1000 (AEST) Received: from localhost ([::1]:41440 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUbN-0005hX-Df for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:55:57 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53136) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVN-0000jE-0m for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVH-0003mh-TH for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37184) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jo-FS; Thu, 14 Jun 2018 11:49:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=Ou/kjAIdcbWc2pYOHDytCCjCr5hUgnrrYcPQIApOtyo=; b=WzhPjvDo/PmRPsVdDcx7uSQxs9UNKUkAfV1IskL23yIXxlwXtiOasL7aH6U4DP6LclDCc+ONokuZeY7iWYSxa4Pun2WFAPO/vYv2ZjEx7jabsHU2Qrj8nbLx1eeSeGkZKt4kqgjVVUZOFSstKiCWgYnl41Dv9oivvPPZF2v0KgX3yuhSvy4pmK/daCFbqRf86ueHUi5jWabb1cAYdNMOiZ2kKCTfDoMAq3kSqTUN8ipDEq34IttXBelRBt4iEsxUxaaIsZfZNyJhWpRr4Csn53y5XDu33EurhEo4RV876lTV0BbzM7dMXEX2dDCnMqXhBk21mjp5x5qAKTIJ39diWQ==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004do-G5; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008NZ-5u; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:48:59 +0300 Message-Id: <0b4e9321b9f77e8a031a09ac3cd2fe6b7a19c6d5.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 02/10] block: Allow changing 'discard' on reopen 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" 'discard' is one of the basic BlockdevOptions available for all drivers, but it's silently ignored by bdrv_reopen_prepare/commit(), so the user cannot change it and doesn't get an error explaining that it can't be changed. Since there's no reason why we shouldn't allow changing it and the implementation is trivial, let's just do it. Signed-off-by: Alberto Garcia --- block.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/block.c b/block.c index 50887087f3..e470db7e5e 100644 --- a/block.c +++ b/block.c @@ -3139,6 +3139,15 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, update_flags_from_options(&reopen_state->flags, opts); + value = qemu_opt_get(opts, "discard"); + if (value != NULL) { + if (bdrv_parse_discard_flags(value, &reopen_state->flags) != 0) { + error_setg(errp, "Invalid discard option"); + ret = -EINVAL; + goto error; + } + } + /* node-name and driver must be unchanged. Put them back into the QDict, so * that they are checked at the end of this function. */ value = qemu_opt_get(opts, "node-name"); From patchwork Thu Jun 14 15:49:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929527 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="LCEcg/vp"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167PQ47sJz9s19 for ; Fri, 15 Jun 2018 01:50:34 +1000 (AEST) Received: from localhost ([::1]:41401 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUW8-0000m5-8g for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:50:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53128) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000jB-Vl for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003mx-0F for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37194) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jm-F9; Thu, 14 Jun 2018 11:49:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=jaDLdJ7t1dBMDI65Xp4rNp7EMLJH/QaRYSY5zHdvaG4=; b=LCEcg/vp9hH/S2QHBiFDRUqF9R4aL3loEkCki5icPc13/evkmG03KKKfzX8O0BKRfHaVavBiqnnxyZ33tTJiBUHSzyGWSOR1rTgu2ac94bOfaLOkw1Ozl2EeMNYsK1BPC8knSXpzfClLchgK06btrUrUftN+hQ53lhQA9RUuc4f3p+shjRmncH7NEZ8lipeAEVvL2P0LXZDHjcWuYbwiA8e9dY2hfxUCGQlTdV0j078gkLvkHfcTgQTwDpYZY/ICTexpxvU6qBAsF7DJSSRSg4EvZlq8ycX9CToa4hQ/wlfDZz9qNEkxYUiwbIWu7k5UIGwCELFpmRTq0zjC4BvsoQ==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dk-C7; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nd-7x; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:01 +0300 Message-Id: <5800c9ccc0a6ce2e58d25b127e75cac282458a11.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 04/10] block: Allow changing 'force-share' on reopen 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" 'force-share' is one of the basic BlockdevOptions available for all drivers, but it's silently ignored by bdrv_reopen_prepare/commit(), so the user cannot change it and doesn't get an error explaining that it can't be changed. Since there's no reason why we shouldn't allow changing it and the implementation is trivial, let's just do it. Signed-off-by: Alberto Garcia --- block.c | 11 +++++++++++ include/block/block.h | 1 + 2 files changed, 12 insertions(+) diff --git a/block.c b/block.c index 4c186e54b6..a741300fae 100644 --- a/block.c +++ b/block.c @@ -3169,6 +3169,16 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, } } + reopen_state->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, + false); + if (reopen_state->force_share && (reopen_state->flags & BDRV_O_RDWR)) { + error_setg(errp, + BDRV_OPT_FORCE_SHARE + "=on can only be used with read-only images"); + ret = -EINVAL; + goto error; + } + /* node-name and driver must be unchanged. Put them back into the QDict, so * that they are checked at the end of this function. */ value = qemu_opt_get(opts, "node-name"); @@ -3300,6 +3310,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) bs->open_flags = reopen_state->flags; bs->read_only = !(reopen_state->flags & BDRV_O_RDWR); bs->detect_zeroes = reopen_state->detect_zeroes; + bs->force_share = reopen_state->force_share; bdrv_refresh_limits(bs, NULL); diff --git a/include/block/block.h b/include/block/block.h index 1e78587331..9306c986ef 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -160,6 +160,7 @@ typedef struct BDRVReopenState { BlockDriverState *bs; int flags; BlockdevDetectZeroesOptions detect_zeroes; + bool force_share; uint64_t perm, shared_perm; QDict *options; QDict *explicit_options; From patchwork Thu Jun 14 15:49:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929532 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="TO9i7aZE"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Sx1bsXz9s19 for ; Fri, 15 Jun 2018 01:53:37 +1000 (AEST) Received: from localhost ([::1]:41419 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUZ4-0003TP-Ne for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:53:34 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53140) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVN-0000jF-1r for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003nU-79 for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37196) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003k1-FL; Thu, 14 Jun 2018 11:49:40 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=fTwdCsJCQcJ9cgnir8tXbvd4eyeykX+0bFKzQ0Yf1fI=; b=TO9i7aZEUh8yHYlHNpbXKpmWMuL+ybD5/hGLrDlc1/g8bMIGp075o4rg89xk7G1cvxF1LrbND8bnV0KytQgMdBRUTVnXAi2t/fuHijp5vs39wkr+zlr3Wdpcstb5GnW/WBAK7bvUDoPXaD2Bjt/z1dzeFr8/oGBXaZP1J7EORZk0PeSklYN2JUdp6BWeOdLQy1CEnU5w/ZOQDpBzpDTYn/i6EP5KE2YEo3tXFkFOJkx8qWC1iKmYDBZOSxo9NUZ0nwHhASAepCglbi4GROq6oaFColfhp1IRZQTu5vRw9VTeDMpqDjjtRB43mfq2H6DvwJ8+//Z5TyBGR3/MLAOgcA==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVF-0004dw-Mz; Thu, 14 Jun 2018 17:49:37 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nf-8t; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:02 +0300 Message-Id: <4a1c8fb9816de2115d08284c36d7a718b5e48de6.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 05/10] block: Add 'keep_old_opts' parameter to bdrv_reopen_queue() 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" The bdrv_reopen_queue() function is used to create a queue with the BDSs that are going to be reopened and their new options. Once the queue is ready bdrv_reopen_multiple() is called to perform the operation. The original options from each one of the BDSs are kept, with the new options passed to bdrv_reopen_queue() applied on top of them. For "x-blockdev-reopen" we want a function that behaves much like "blockdev-add". We want to ignore the previous set of options so that only the ones actually specified by the user are applied, with the rest having their default values. We can achieve this by adding a new parameter to bdrv_reopen_queue() that specifies whether the old set of options is kept or discarded when building the reopen queue. All current callers will set that parameter to true, but x-blockdev-reopen will set it to false. Signed-off-by: Alberto Garcia --- block.c | 34 +++++++++++++++++++--------------- block/replication.c | 4 ++-- include/block/block.h | 3 ++- qemu-io-cmds.c | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/block.c b/block.c index a741300fae..0b9268a48d 100644 --- a/block.c +++ b/block.c @@ -2850,7 +2850,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, int flags, const BdrvChildRole *role, QDict *parent_options, - int parent_flags) + int parent_flags, + bool keep_old_opts) { assert(bs != NULL); @@ -2899,13 +2900,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, } /* Old explicitly set values (don't overwrite by inherited value) */ - if (bs_entry) { - old_options = qdict_clone_shallow(bs_entry->state.explicit_options); - } else { - old_options = qdict_clone_shallow(bs->explicit_options); + if (bs_entry || keep_old_opts) { + old_options = qdict_clone_shallow(bs_entry ? + bs_entry->state.explicit_options : + bs->explicit_options); + bdrv_join_options(bs, options, old_options); + qobject_unref(old_options); } - bdrv_join_options(bs, options, old_options); - qobject_unref(old_options); explicit_options = qdict_clone_shallow(options); @@ -2923,10 +2924,12 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, qobject_unref(options_copy); } - /* Old values are used for options that aren't set yet */ - old_options = qdict_clone_shallow(bs->options); - bdrv_join_options(bs, options, old_options); - qobject_unref(old_options); + if (keep_old_opts) { + /* Old values are used for options that aren't set yet */ + old_options = qdict_clone_shallow(bs->options); + bdrv_join_options(bs, options, old_options); + qobject_unref(old_options); + } /* bdrv_open_inherit() sets and clears some additional flags internally */ flags &= ~BDRV_O_PROTOCOL; @@ -2967,7 +2970,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, g_free(child_key_dot); bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0, - child->role, options, flags); + child->role, options, flags, keep_old_opts); } return bs_queue; @@ -2975,10 +2978,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, - QDict *options, int flags) + QDict *options, int flags, + bool keep_old_opts) { return bdrv_reopen_queue_child(bs_queue, bs, options, flags, - NULL, NULL, 0); + NULL, NULL, 0, keep_old_opts); } /* @@ -3049,7 +3053,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) bdrv_subtree_drained_begin(bs); - queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); + queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags, true); ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); diff --git a/block/replication.c b/block/replication.c index 826db7b304..af984c29dd 100644 --- a/block/replication.c +++ b/block/replication.c @@ -401,12 +401,12 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, if (orig_hidden_flags != new_hidden_flags) { reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, - new_hidden_flags); + new_hidden_flags, true); } if (!(orig_secondary_flags & BDRV_O_RDWR)) { reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs, - NULL, new_secondary_flags); + NULL, new_secondary_flags, true); } if (reopen_queue) { diff --git a/include/block/block.h b/include/block/block.h index 9306c986ef..c8654dde65 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -267,7 +267,8 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, - QDict *options, int flags); + QDict *options, int flags, + bool keep_old_opts); int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp); int bdrv_reopen_prepare(BDRVReopenState *reopen_state, diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 5bf5f28178..4742358b1a 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -2050,7 +2050,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) qemu_opts_reset(&reopen_opts); bdrv_subtree_drained_begin(bs); - brq = bdrv_reopen_queue(NULL, bs, opts, flags); + brq = bdrv_reopen_queue(NULL, bs, opts, flags, true); bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err); bdrv_subtree_drained_end(bs); From patchwork Thu Jun 14 15:49:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929540 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="bMKHR3G0"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167ZL3Flxz9ry1 for ; Fri, 15 Jun 2018 01:58:18 +1000 (AEST) Received: from localhost ([::1]:41453 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUdc-0007Sm-6h for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:58:16 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53131) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVN-0000jC-03 for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003ne-7x for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37191) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003js-Fe; Thu, 14 Jun 2018 11:49:40 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=YyAf3UHdDJUHZMorfbszAnQm+Tk9GD+yx2XKD0VTfH4=; b=bMKHR3G0qUSYWX3rZAuHLgMr891HWBG2ps//6U90Jo5D+WLZH/m6voqRiuiqOzuKLMhyHCs7PlG6IfQWGB+I0yA3dyz9UTmDlwYEDkReyxNzECreMkfPz42SfoNQ5oxmRl6YEfqChVa8aEWTpe9ifHZV99o/FQm3ypR7U7Fl/LtOOrILGYDGQlhR+IW1X1xHKA32I4uWQNGG0h+fZVcbl29YccBCCMuRYHFy2AwLAqFwAxgvmnuiXyzEeiHBv8kgRDgfl2IAiDok7pnIUB/DwPiPW02ytpDN9QsuwTc9Xra6v79ksdipI8G5fwh4AaY47apCivHorLidcjrFJts/zw==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dq-Er; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nh-A0; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:03 +0300 Message-Id: <6782db91216488e4fc1509a267a362a3f15a1aaa.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 06/10] block: Allow changing the backing file on reopen 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" This patch allows the user to change the backing file of an image that is being reopened. Here's what it does: - In bdrv_reopen_queue_child(): if the 'backing' option points to an image different from the current backing file then it means that the latter is going be detached so it must not be put in the reopen queue. - In bdrv_reopen_prepare(): check that the value of 'backing' points to an existing node or is null. If it points to an existing node it also needs to make sure that replacing the backing file will not create a cycle in the node graph (i.e. you cannot reach the parent from the new backing file). - In bdrv_reopen_commit(): perform the actual node replacement by calling bdrv_set_backing_hd(). It may happen that there are temporary implicit nodes between the BDS that we are reopening and the backing file that we want to replace (e.g. a commit fiter node), so we must skip them. Although x-blockdev-reopen is meant to be used like blockdev-add, there's two important things that must be taken into account: 1) The only way to set a new backing file is by using a reference to an existing node (previously added with e.g. blockdev-add). If 'backing' contains a dictionary with a new set of options ({"driver": "qcow2", "file": { ... }}) then it is interpreted that the _existing_ backing file must be reopened with those options. 2) If the 'backing' option is omitted altogether then the existing backing file (if any) is kept. Unlike blockdev-add this will _not_ open the default backing file specified in the image header. Signed-off-by: Alberto Garcia --- block.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 0b9268a48d..91fff6361f 100644 --- a/block.c +++ b/block.c @@ -2823,6 +2823,27 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, } /* + * Returns true if @child can be reached recursively from @bs + */ +static bool bdrv_recurse_has_child(BlockDriverState *bs, + BlockDriverState *child) +{ + BdrvChild *c; + + if (bs == child) { + return true; + } + + QLIST_FOREACH(c, &bs->children, next) { + if (bdrv_recurse_has_child(c->bs, child)) { + return true; + } + } + + return false; +} + +/* * Adds a BlockDriverState to a simple queue for an atomic, transactional * reopen of multiple devices. * @@ -2957,6 +2978,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, QLIST_FOREACH(child, &bs->children, next) { QDict *new_child_options; char *child_key_dot; + bool child_keep_old = keep_old_opts; /* reopen can only change the options of block devices that were * implicitly created and inherited options. For other (referenced) @@ -2965,12 +2987,28 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, continue; } + /* If the 'backing' option is set and it points to a different + * node then it means that we want to replace the current one, + * so we shouldn't put it in the reopen queue */ + if (child->role == &child_backing && qdict_haskey(options, "backing")) { + const char *backing = qdict_get_try_str(options, "backing"); + if (g_strcmp0(backing, child->bs->node_name)) { + continue; + } + } + child_key_dot = g_strdup_printf("%s.", child->name); qdict_extract_subqdict(options, &new_child_options, child_key_dot); g_free(child_key_dot); + /* If the 'backing' option was omitted then we keep the old + * set of options */ + if (child->role == &child_backing && !qdict_size(new_child_options)) { + child_keep_old = true; + } + bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0, - child->role, options, flags, keep_old_opts); + child->role, options, flags, child_keep_old); } return bs_queue; @@ -3262,7 +3300,34 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, * the user can simply omit options which cannot be changed anyway, * so they will stay unchanged. */ - if (!qobject_is_equal(new, old)) { + /* + * Allow changing the 'backing' option. The new value can be + * either a reference to an existing node (using its node name) + * or NULL to simply detach the current backing file. + */ + if (!strcmp(entry->key, "backing")) { + switch (qobject_type(new)) { + case QTYPE_QNULL: + break; + case QTYPE_QSTRING: { + const char *str = qobject_get_try_str(new); + BlockDriverState *bs = bdrv_lookup_bs(NULL, str, errp); + if (bs == NULL) { + ret = -EINVAL; + goto error; + } else if (bdrv_recurse_has_child(bs, reopen_state->bs)) { + error_setg(errp, "Making '%s' a backing file of '%s' " + "would create a cycle", str, + reopen_state->bs->node_name); + ret = -EINVAL; + goto error; + } + break; + } + default: + g_assert_not_reached(); + } + } else if (!qobject_is_equal(new, old)) { error_setg(errp, "Cannot change the option '%s'", entry->key); ret = -EINVAL; goto error; @@ -3293,6 +3358,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) BlockDriver *drv; BlockDriverState *bs; bool old_can_write, new_can_write; + QObject *qobj; assert(reopen_state != NULL); bs = reopen_state->bs; @@ -3307,6 +3373,22 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) drv->bdrv_reopen_commit(reopen_state); } + /* Change the backing file if a new one was specified */ + qobj = qdict_get(reopen_state->options, "backing"); + if (qobj) { + const char *str = qobject_get_try_str(qobj); + BlockDriverState *new_backing = str ? bdrv_find_node(str) : NULL; + BlockDriverState *overlay = bs; + /* If there are implicit nodes between the current BDS and its + * "actual" backing file, skip them */ + while (backing_bs(overlay) && backing_bs(overlay)->implicit) { + overlay = backing_bs(overlay); + } + if (new_backing != backing_bs(overlay)) { + bdrv_set_backing_hd(overlay, new_backing, NULL); + } + } + /* set BDS specific flags now */ qobject_unref(bs->explicit_options); From patchwork Thu Jun 14 15:49:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929538 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="YesLJOwA"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Wd4tkYz9ry1 for ; Fri, 15 Jun 2018 01:55:57 +1000 (AEST) Received: from localhost ([::1]:41438 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUbL-0005fp-AP for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:55:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53124) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000j8-V4 for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003nj-8y for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:45 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37192) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jp-FM; Thu, 14 Jun 2018 11:49:40 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=mZMN9LoMOP8fKEbQlUgjrHThoV3ojGQ1mzHn64GAiu8=; b=YesLJOwAm/d+bymC2901TcxzJItKaepifqmZuG845Alai14k6iGimd5yOjif6qH8YbzANxCGrXT6LWs39FcHawGWR65SH6zzcrswa/poLay45/wfIhJpWdJ5DVJxLKA80kKpcOe3b3Kv+sU6NDidS0/qk6T73ljfAKYZmjeoU66gls0yvuiOWLct/xfLex2ltXo4Tp4grpfEiYJBa2Fp+s3kWatQcMPaUanUalL/cCxS5XpbUYuV/0JCEv5I+c+LdtOg/lZwWojCgc+ouOYrvV40ffca25zUnbFUImCZGWrZnv4/yftVLoyakDGf67kMi/9Rs6J2YRpO9ddg1qbGHQ==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dj-E8; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nj-BI; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:04 +0300 Message-Id: <54751f9032d94639042f0d502b1731a7b89f9099.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 07/10] block: Add 'runtime_opts' and 'mutable_opts' fields to BlockDriver 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" This patch adds two new fields to BlockDriver: - runtime_opts: list of runtime options for a particular block driver. We'll use this list later to detect what options are missing when we try to reopen a block device. - mutable_opts: names of the runtime options that can be modified after a block device has been added. This way an option can not be changed unless we explicitly allow it. This also sets runtime_opts (and mutable_opts where appropriate) in all drivers that allow reopening. Most of those drivers don't actually support changing any of their options. If the user specifies a new value for an option that can't be changed then we already detect that and forbid it (in bdrv_reopen_prepare()). But if the user omits an option in order to try to reset it to its default value we need to detect that, so we'll use these two new fields for that. Signed-off-by: Alberto Garcia --- block/blkdebug.c | 1 + block/crypto.c | 1 + block/file-posix.c | 10 ++++++++++ block/iscsi.c | 2 ++ block/null.c | 2 ++ block/nvme.c | 1 + block/qcow.c | 1 + block/rbd.c | 1 + block/sheepdog.c | 2 ++ block/vpc.c | 1 + include/block/block_int.h | 4 ++++ 11 files changed, 26 insertions(+) diff --git a/block/blkdebug.c b/block/blkdebug.c index 526af2a808..9b52465642 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -893,6 +893,7 @@ static BlockDriver bdrv_blkdebug = { .protocol_name = "blkdebug", .instance_size = sizeof(BDRVBlkdebugState), .is_filter = true, + .runtime_opts = &runtime_opts, .bdrv_parse_filename = blkdebug_parse_filename, .bdrv_file_open = blkdebug_open, diff --git a/block/crypto.c b/block/crypto.c index bc322b50f5..5da97799b2 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -696,6 +696,7 @@ BlockDriver bdrv_crypto_luks = { .bdrv_co_create_opts = block_crypto_co_create_opts_luks, .bdrv_truncate = block_crypto_truncate, .create_opts = &block_crypto_create_opts_luks, + .runtime_opts = &block_crypto_runtime_opts_luks, .bdrv_reopen_prepare = block_crypto_reopen_prepare, .bdrv_refresh_limits = block_crypto_refresh_limits, diff --git a/block/file-posix.c b/block/file-posix.c index 1511a4d69d..d6fe8d17cf 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -429,6 +429,8 @@ static QemuOptsList raw_runtime_opts = { }, }; +static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL }; + static int raw_open_common(BlockDriverState *bs, QDict *options, int bdrv_flags, int open_flags, Error **errp) { @@ -2582,6 +2584,8 @@ BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", .instance_size = sizeof(BDRVRawState), + .runtime_opts = &raw_runtime_opts, + .mutable_opts = mutable_opts, .bdrv_needs_filename = true, .bdrv_probe = NULL, /* no probe for protocols */ .bdrv_parse_filename = raw_parse_filename, @@ -3064,6 +3068,8 @@ static BlockDriver bdrv_host_device = { .format_name = "host_device", .protocol_name = "host_device", .instance_size = sizeof(BDRVRawState), + .runtime_opts = &raw_runtime_opts, + .mutable_opts = mutable_opts, .bdrv_needs_filename = true, .bdrv_probe_device = hdev_probe_device, .bdrv_parse_filename = hdev_parse_filename, @@ -3189,6 +3195,8 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), + .runtime_opts = &raw_runtime_opts, + .mutable_opts = mutable_opts, .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, @@ -3321,6 +3329,8 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), + .runtime_opts = &raw_runtime_opts, + .mutable_opts = mutable_opts, .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, diff --git a/block/iscsi.c b/block/iscsi.c index c2fbd8a8aa..5f68d11176 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2443,6 +2443,7 @@ static BlockDriver bdrv_iscsi = { .bdrv_close = iscsi_close, .bdrv_co_create_opts = iscsi_co_create_opts, .create_opts = &iscsi_create_opts, + .runtime_opts = &runtime_opts, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, @@ -2480,6 +2481,7 @@ static BlockDriver bdrv_iser = { .bdrv_close = iscsi_close, .bdrv_co_create_opts = iscsi_co_create_opts, .create_opts = &iscsi_create_opts, + .runtime_opts = &runtime_opts, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, diff --git a/block/null.c b/block/null.c index 5d610fdfba..fff3b12099 100644 --- a/block/null.c +++ b/block/null.c @@ -260,6 +260,7 @@ static BlockDriver bdrv_null_co = { .format_name = "null-co", .protocol_name = "null-co", .instance_size = sizeof(BDRVNullState), + .runtime_opts = &runtime_opts, .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, @@ -280,6 +281,7 @@ static BlockDriver bdrv_null_aio = { .format_name = "null-aio", .protocol_name = "null-aio", .instance_size = sizeof(BDRVNullState), + .runtime_opts = &runtime_opts, .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, diff --git a/block/nvme.c b/block/nvme.c index 6f71122bf5..5c77f82adb 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1158,6 +1158,7 @@ static BlockDriver bdrv_nvme = { .format_name = "nvme", .protocol_name = "nvme", .instance_size = sizeof(BDRVNVMeState), + .runtime_opts = &runtime_opts, .bdrv_parse_filename = nvme_parse_filename, .bdrv_file_open = nvme_file_open, diff --git a/block/qcow.c b/block/qcow.c index 1f866af0d3..1176d5dbef 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -1188,6 +1188,7 @@ static QemuOptsList qcow_create_opts = { static BlockDriver bdrv_qcow = { .format_name = "qcow", .instance_size = sizeof(BDRVQcowState), + .runtime_opts = &qcow_runtime_opts, .bdrv_probe = qcow_probe, .bdrv_open = qcow_open, .bdrv_close = qcow_close, diff --git a/block/rbd.c b/block/rbd.c index a16431e267..173d7a50b0 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1160,6 +1160,7 @@ static QemuOptsList qemu_rbd_create_opts = { static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), + .runtime_opts = &runtime_opts, .bdrv_parse_filename = qemu_rbd_parse_filename, .bdrv_refresh_limits = qemu_rbd_refresh_limits, .bdrv_file_open = qemu_rbd_open, diff --git a/block/sheepdog.c b/block/sheepdog.c index 7b98725af7..6b27df169a 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -3301,6 +3301,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_attach_aio_context = sd_attach_aio_context, .create_opts = &sd_create_opts, + .runtime_opts = &runtime_opts, }; static BlockDriver bdrv_sheepdog_unix = { @@ -3338,6 +3339,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_attach_aio_context = sd_attach_aio_context, .create_opts = &sd_create_opts, + .runtime_opts = &runtime_opts, }; static void bdrv_sheepdog_init(void) diff --git a/block/vpc.c b/block/vpc.c index 0ebfcd3cc8..460212c261 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -1234,6 +1234,7 @@ static BlockDriver bdrv_vpc = { .bdrv_get_info = vpc_get_info, .create_opts = &vpc_create_opts, + .runtime_opts = &vpc_runtime_opts, .bdrv_has_zero_init = vpc_has_zero_init, }; diff --git a/include/block/block_int.h b/include/block/block_int.h index 327e478a73..eef3a55220 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -342,6 +342,10 @@ struct BlockDriver { /* List of options for creating images, terminated by name == NULL */ QemuOptsList *create_opts; + /* Runtime options for a block device, terminated by name == NULL */ + QemuOptsList *runtime_opts; + /* Names of the runtime options that can be modified, NULL-terminated */ + const char *const *mutable_opts; /* * Returns 0 for completed check, -errno for internal errors. From patchwork Thu Jun 14 15:49:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929533 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="V/Kt3xM+"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167T22d0bz9s19 for ; Fri, 15 Jun 2018 01:53:42 +1000 (AEST) Received: from localhost ([::1]:41421 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUZ9-0003WV-Vn for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:53:40 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53123) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000j7-V2 for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003n0-0D for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37187) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jr-F5; Thu, 14 Jun 2018 11:49:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=LRV5uHebYx5DR05SranVems7bTU98nW4HAHjsoJPdc4=; b=V/Kt3xM+LEcFSzt4WVUmjSZuAGbqmgHQE6m/nO/GwyPIVl7qbTpJYzdQPkg6Ej2QGjUz0ymlqlPWfv2wyTlZ4MUv4Fw6xNkE/dnjOKupnFkzBQ36sKjAcApOLhUO/ejpKHNENNVPLJDGSH/4McZSbKx5eLdC1bPjkf+Fgp9+nWOY6MzJd4HBptn0HFpGbOCcfpAhh3ZPxJ4osJVfLXdtiBQsnvjqofL49vG4MrfSwY636xzdtX1ZfsOGDMGWk4ccP3YLuk+ZTNT171rvArOpd6syeZCJCCxMOEAt4DCS61jxUdDzNPOchI4laAIB4nCpmcZ8juxEy0qvpBodPito5A==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dl-Eh; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nl-CB; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:05 +0300 Message-Id: X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 08/10] block: Add bdrv_reset_options_allowed() 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" bdrv_reopen_prepare() receives a BDRVReopenState with (among other things) a new set of options to be applied to that BlockDriverState. If an option is missing then it means that we want to reset it to its default value rather than keeping the previous one. This way the state of the block device after being reopened is comparable to that of a device added with "blockdev-add" using the same set of options. Not all options from all drivers can be changed, however. If the user attempts to reset an immutable option to its default value using this method then we must forbid it. This new function takes a QemuOptsList with the options of a block driver and checks if there's any that was explicitly set but is not present in the BDRVReopenState. If the option is present in both sets we don't need to check that it keeps the same value. The loop at the end of bdrv_reopen_prepare() already takes care of that. Signed-off-by: Alberto Garcia --- block.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/block.c b/block.c index 91fff6361f..4aa40f7f10 100644 --- a/block.c +++ b/block.c @@ -2823,6 +2823,43 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, } /* + * For every option in @list, check that if it is present in + * @state->bs->explicit_options then it is also present in + * @state->options. Options present in @mutable_opts are skipped. + * + * @mutable_opts is either NULL or a NULL-terminated array of option + * names. + * + * Return 0 on success, -EINVAL otherwise. + */ +static int bdrv_reset_options_allowed(BDRVReopenState *state, + QemuOptsList *list, + const char *const mutable_opts[], + Error **errp) +{ + QemuOptDesc *desc; + for (desc = list->desc; desc && desc->name; desc++) { + unsigned i; + bool skip_option = false; + for (i = 0; mutable_opts && mutable_opts[i] != 0; i++) { + if (!strcmp(desc->name, mutable_opts[i])) { + skip_option = true; + break; + } + } + + if (!skip_option && !qdict_haskey(state->options, desc->name) && + qdict_haskey(state->bs->explicit_options, desc->name)) { + error_setg(errp, "Option '%s' can't be reset to its default value", + desc->name); + return -EINVAL; + } + } + + return 0; +} + +/* * Returns true if @child can be reached recursively from @bs */ static bool bdrv_recurse_has_child(BlockDriverState *bs, @@ -3254,6 +3291,18 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, } if (drv->bdrv_reopen_prepare) { + /* If a driver-specified option is missing, it means that we + * should reset it to its default value. Not all options can + * be modified, so we need to check that first */ + if (drv->runtime_opts) { + ret = bdrv_reset_options_allowed(reopen_state, drv->runtime_opts, + drv->mutable_opts, &local_err); + if (ret) { + error_propagate(errp, local_err); + goto error; + } + } + ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); if (ret) { if (local_err != NULL) { From patchwork Thu Jun 14 15:49:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929530 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="gaOYvYXS"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Pm1d8Jz9s19 for ; Fri, 15 Jun 2018 01:50:52 +1000 (AEST) Received: from localhost ([::1]:41405 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUWP-00017r-Tx for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:50:49 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53121) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000j5-U9 for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVH-0003mt-Vp for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37185) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jl-FQ; Thu, 14 Jun 2018 11:49:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=WwUCuqhsG34Agbie0AV7VecuEqDQ4CKoWVhDxv54esU=; b=gaOYvYXSce1rEYxW8bs77F7CuMRgJxavjGhlr3GCKy/6NgmPk2Tm6NLntYitR7AHgLM3PhXyPuHJSKydGJNfymwHcGqpuq4yQUY1DjDr3EviESIMG9JYfX0s7dL/j2z4LS8IOm9ImzkdEoOLZWlufE67/ao/o2+qUL9VNyOwYYR2ghD95t7s90QeOYz0yErwCIjd8Fj1COx4PiomyUKTgHhfHBkqhw5RXo0+6yD9Q8fScBwKm2dfPh2Qko71QPCMBS7r4Wvp6S7gDM8xaQXZIWxs5whDdJG81UgP6cjQmoIExGzRg9dsZgAKKwllojKCggquth4TKF4KN8tKD8T98A==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dm-EX; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nn-DF; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:06 +0300 Message-Id: <4912b75d514ba7b01def0a79e7a40146d0a88fca.1528991017.git.berto@igalia.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 09/10] block: Add a 'x-blockdev-reopen' QMP command 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" This command allows reopening an arbitrary BlockDriverState with a new set of options. Some options (e.g node-name) cannot be changed and some block drivers don't allow reopening, but otherwise this command is modelled after 'blockdev-add' and the state of the reopened BlockDriverState should generally be the same as if it had just been added by 'blockdev-add' with the same set of options. One notable exception is that when the 'backing' option is omitted then 'blockdev-add' opens the image's default backing file, whereas 'x-blockdev-reopen' simply keeps the current one. This command allows reconfiguring the graph by using the appropriate options to change the children of a node. At the moment it's possible to change a backing file by setting the 'backing' option to the name of the new node, but it should also be possible to add a similar functionality to other block drivers (e.g. Quorum, blkverify). Although the API is unlikely to change, this command is marked experimental for the time being so there's room to see if the semantics need changes. Signed-off-by: Alberto Garcia --- blockdev.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 29 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/blockdev.c b/blockdev.c index 4862323012..1502747483 100644 --- a/blockdev.c +++ b/blockdev.c @@ -4236,6 +4236,63 @@ fail: visit_free(v); } +void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) +{ + BlockDriverState *bs; + QObject *obj; + Visitor *v = qobject_output_visitor_new(&obj); + Error *local_err = NULL; + int flags; + BlockReopenQueue *queue; + QDict *qdict; + + /* Check for the selected node name */ + if (!options->has_node_name) { + error_setg(errp, "Node name not specified"); + goto fail; + } + + bs = bdrv_find_node(options->node_name); + if (!bs) { + error_setg(errp, "Cannot find node named '%s'", options->node_name); + goto fail; + } + + /* Put all options in a QDict and flatten it */ + visit_type_BlockdevOptions(v, NULL, &options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + visit_complete(v, &obj); + qdict = qobject_to(QDict, obj); + + qdict_flatten(qdict); + + /* Set some default values */ + qdict_put_bool(qdict, BDRV_OPT_CACHE_DIRECT, + qdict_get_try_bool(qdict, BDRV_OPT_CACHE_DIRECT, false)); + qdict_put_bool(qdict, BDRV_OPT_CACHE_NO_FLUSH, + qdict_get_try_bool(qdict, BDRV_OPT_CACHE_NO_FLUSH, false)); + qdict_put_bool(qdict, BDRV_OPT_READ_ONLY, + qdict_get_try_bool(qdict, BDRV_OPT_READ_ONLY, false)); + + flags = 0; + if (!qdict_get_bool(qdict, BDRV_OPT_READ_ONLY)) { + flags |= (BDRV_O_RDWR | BDRV_O_ALLOW_RDWR); + } + + /* Perform the reopen operation */ + bdrv_subtree_drained_begin(bs); + queue = bdrv_reopen_queue(NULL, bs, qdict, flags, false); + bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, errp); + bdrv_subtree_drained_end(bs); + +fail: + visit_free(v); +} + void qmp_blockdev_del(const char *node_name, Error **errp) { AioContext *aio_context; diff --git a/qapi/block-core.json b/qapi/block-core.json index fff23fc82b..ee8bf7c0ea 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3651,6 +3651,35 @@ { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } ## +# @x-blockdev-reopen: +# +# Reopens a block device using the given set of options. Any option +# not specified will be reset to its default value regardless of its +# previous status. If an option cannot be changed or a particular +# driver does not support reopening the command will return an error. +# +# The top-level @node-name option (from BlockdevOptions) must be +# specified and is used to select the block device to be reopened. +# Other @node-name options must be either omitted or set to the +# current name of the appropriate node. This command won't change any +# node name and any attempt to do it will result in an error. +# +# The @backing option can always be omitted: any block device can be +# reopened by specifying only its own options, without having to +# include any of its backing files. +# +# Unlike blockdev-add, leaving out the @backing option will simply +# keep the current backing file for that image and will not try to +# open the default one. The way to replace a backing file is by +# passing a reference to the new one in the @backing parameter. +# If @backing is null then the current backing file will be removed. +# +# Since: 2.13 +## +{ 'command': 'x-blockdev-reopen', + 'data': 'BlockdevOptions', 'boxed': true } + +## # @blockdev-del: # # Deletes a block device that has been added using blockdev-add. From patchwork Thu Jun 14 15:49:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Garcia X-Patchwork-Id: 929541 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=2001:4830:134:3::11; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=igalia.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=igalia.com header.i=@igalia.com header.b="aXBS2ccX"; dkim-atps=neutral Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4167Zl43Bmz9ry1 for ; Fri, 15 Jun 2018 01:58:39 +1000 (AEST) Received: from localhost ([::1]:41456 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUdx-0007it-7a for incoming@patchwork.ozlabs.org; Thu, 14 Jun 2018 11:58:37 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:53127) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTUVM-0000jA-Vt for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:50 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTUVI-0003nZ-7D for qemu-devel@nongnu.org; Thu, 14 Jun 2018 11:49:44 -0400 Received: from fanzine.igalia.com ([91.117.99.155]:37195) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fTUVH-0003jn-Eu; Thu, 14 Jun 2018 11:49:40 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=References:In-Reply-To:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=GvkfqoIVfcczg0SUqRV1QHpf5324jUK1yZIkvexgr58=; b=aXBS2ccXUR6kYc9r1/iYxMAhDUXHJR2gnCWLDf3vHfhPBcLqeHZ9oxFMx1K1hyXv+EXxbsxBQ17cvFMbHTE45SpXF4p+hE9OvmmRydlU9rF2urzC39qdeuw8ftX31/P76m2g5kqUHPiBVbAiVvItMgHzPL8s3ZEluK8XrIA7qtVb/NyPEYGVSv9xo3//7skwVrq2Hr/eiBtp48tAwxGD8EFj8VZk201IQgINpG8bpFXx8hxmvnTU+nwp6muoPQxjZcucTyx1ZtpcPAlXY8T7T6cW98UhGkBt5Rgw2SYuSmIxlAq0tb6fKLV5gnkTEZg04pVhrOc9ADHpE9Tz2pPkEA==; Received: from [194.100.51.2] (helo=perseus.local) by fanzine.igalia.com with esmtpsa (Cipher TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim) id 1fTUVE-0004dp-FW; Thu, 14 Jun 2018 17:49:36 +0200 Received: from berto by perseus.local with local (Exim 4.89) (envelope-from ) id 1fTUUv-0008Nu-E7; Thu, 14 Jun 2018 18:49:17 +0300 From: Alberto Garcia To: qemu-devel@nongnu.org Date: Thu, 14 Jun 2018 18:49:07 +0300 Message-Id: X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.117.99.155 Subject: [Qemu-devel] [RFC PATCH 10/10] qemu-iotests: Test the x-blockdev-reopen QMP command 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: Kevin Wolf , Alberto Garcia , qemu-block@nongnu.org, Markus Armbruster , Max Reitz Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" This patch adds several tests for the x-blockdev-reopen QMP command. Signed-off-by: Alberto Garcia --- tests/qemu-iotests/220 | 724 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/220.out | 5 + tests/qemu-iotests/group | 1 + 3 files changed, 730 insertions(+) create mode 100755 tests/qemu-iotests/220 create mode 100644 tests/qemu-iotests/220.out diff --git a/tests/qemu-iotests/220 b/tests/qemu-iotests/220 new file mode 100755 index 0000000000..ac737845d8 --- /dev/null +++ b/tests/qemu-iotests/220 @@ -0,0 +1,724 @@ +#!/usr/bin/env python +# +# Test cases for the QMP 'x-blockdev-reopen' command +# +# Copyright (C) 2018 Igalia, S.L. +# Author: Alberto Garcia +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import re +import iotests +import copy +import json +from iotests import qemu_img, qemu_io + +hd_path = [ + os.path.join(iotests.test_dir, 'hd0.img'), + os.path.join(iotests.test_dir, 'hd1.img'), + os.path.join(iotests.test_dir, 'hd2.img') +] + +def hd_opts(idx): + return {'driver': iotests.imgfmt, + 'node-name': 'hd%d' % idx, + 'file': {'driver': 'file', + 'node-name': 'hd%d-file' % idx, + 'filename': hd_path[idx] } } + +class TestBlockdevReopen(iotests.QMPTestCase): + total_io_cmds = 0 + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M') + qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1]) + qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M') + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0]) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1]) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2]) + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + self.check_qemu_io_errors() + os.remove(hd_path[0]) + os.remove(hd_path[1]) + os.remove(hd_path[2]) + + # The output of qemu-io is not returned by vm.hmp_qemu_io() but + # it's stored in the log and can only be read when the VM has been + # shut down. This function runs qemu-io and keeps track of the + # number of times it's been called. + def run_qemu_io(self, img, cmd): + result = self.vm.hmp_qemu_io(img, cmd) + self.assert_qmp(result, 'return', '') + self.total_io_cmds += 1 + + # Once the VM is shut down we can parse the log and see if qemu-io + # ran without errors. + def check_qemu_io_errors(self): + self.assertFalse(self.vm.is_running()) + found = 0 + log = self.vm.get_log() + for line in log.split("\n"): + if line.startswith("Pattern verification failed"): + raise Exception("%s (command #%d)" % (line, found)) + if re.match("read .*/.* bytes at offset", line): + found += 1 + self.assertEqual(found, self.total_io_cmds, + "Expected output of %d qemu-io commands, found %d" % + (found, self.total_io_cmds)) + + # Run x-blockdev-reopen with 'opts' but applying 'newopts' + # on top of it. The original 'opts' dict is unmodified + def reopen(self, opts, newopts = {}, errmsg = None): + opts = copy.deepcopy(opts) + + # Apply the changes from 'newopts' on top of 'opts' + for key in newopts: + value = newopts[key] + # If key has the form "foo.bar" then we need to do + # opts["foo"]["bar"] = value, not opts["foo.bar"] = value + subdict = opts + while key.find('.') != -1: + [prefix, key] = key.split('.', 1) + subdict = opts[prefix] + subdict[key] = value + + result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts) + if errmsg: + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', errmsg) + else: + self.assert_qmp(result, 'return', {}) + + + # Run query-named-block-nodes and return the specified entry + def get_node(self, node_name): + result = self.vm.qmp('query-named-block-nodes') + for node in result['return']: + if node['node-name'] == node_name: + return node + return None + + # Run 'query-named-block-nodes' and compare its output with the + # value passed by the user in 'graph' + def check_node_graph(self, graph): + result = self.vm.qmp('query-named-block-nodes') + self.assertEqual(json.dumps(graph, sort_keys=True), + json.dumps(result, sort_keys=True)) + + # This test opens one single disk image (without backing files) + # and tries to reopen it with illegal / incorrect parameters. + def test_incorrect_parameters_single_file(self): + # Open 'hd0' only (no backing files) + opts = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can reopen the image passing the same options + self.reopen(opts) + + # We cannot change any of these + self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'") + self.reopen(opts, {'node-name': ''}, "Cannot find node named ''") + self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string") + self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") + self.reopen(opts, {'driver': ''}, "Invalid parameter ''") + self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string") + self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'") + self.reopen(opts, {'file': ''}, "Cannot change the option 'file'") + self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef") + self.reopen(opts, {'file': 'hd0-file'}, "Cannot change the option 'file'") + self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'") + self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'") + self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'") + self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string") + + # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it + del opts['node-name'] + self.reopen(opts, {}, "Node name not specified") + + # Check that nothing has changed + self.check_node_graph(original_graph) + + # Remove the node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # This test opens an image with a backing file and tries to reopen + # it with illegal / incorrect parameters. + def test_incorrect_parameters_backing_file(self): + # Open hd1 omitting the backing options (hd0 will be opened + # with the default options) + opts = hd_opts(1) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can reopen the image passing the same options + self.reopen(opts) + + # We can't use a non-existing or empty (non-NULL) node as the backing image + self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found") + self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=") + + # We can reopen the image just fine if we specify the backing options + opts['backing'] = {'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': hd_path[0]}} + self.reopen(opts) + + # We cannot change any of these options + self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'") + self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'") + + # Check that nothing has changed since the beginning + self.check_node_graph(original_graph) + + # Remove the node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') + self.assert_qmp(result, 'return', {}) + + def test_reopen(self): + # Open the hd1 image passing all backing options + opts = hd_opts(1) + opts['backing'] = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can reopen the image passing the same options + self.reopen(opts) + + # Reopen in read-only mode + self.assert_qmp(self.get_node('hd1'), 'ro', False) + + self.reopen(opts, {'read-only': True}) + self.assert_qmp(self.get_node('hd1'), 'ro', True) + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'ro', False) + + # Change the cache options + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) + self.reopen(opts, {'cache': { 'direct': True, 'no-flush': True }}) + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', True) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True) + + # Reopen again with the original options + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) + + # Change 'detect-zeroes' and 'discard' + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') + self.reopen(opts, {'detect-zeroes': 'on'}) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') + self.reopen(opts, {'detect-zeroes': 'unmap'}, + "setting detect-zeroes to unmap is not allowed " + + "without setting discard operation to unmap") + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') + self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'}) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap') + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') + + # Change 'force-share' + self.reopen(opts, {'force-share': True}, + "force-share=on can only be used with read-only images") + self.reopen(opts, {'force-share': True, 'read-only': True}) + self.reopen(opts, {'force-share': False}) + + # Change some qcow2-specific options + # No way to test for success other than checking the return message + if iotests.imgfmt == 'qcow2': + self.reopen(opts, {'l2-cache-entry-size': 128 * 1024}, + "L2 cache entry size must be a power of two "+ + "between 512 and the cluster size (65536)") + self.reopen(opts, {'l2-cache-size': 1024 * 1024, + 'cache-size': 512 * 1024}, + "l2-cache-size may not exceed cache-size") + self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024, + 'refcount-cache-size': 4 * 1024 * 1024, + 'l2-cache-entry-size': 32 * 1024}) + self.reopen(opts, {'pass-discard-request': True}) + + # Check that nothing has changed since the beginning + # (from the parameters that we can check) + self.check_node_graph(original_graph) + + # Check that the node names (other than the top-level one) are optional + del opts['file']['node-name'] + del opts['backing']['node-name'] + del opts['backing']['file']['node-name'] + self.reopen(opts) + self.check_node_graph(original_graph) + + # Reopen setting backing = null, this removes the backing image from the chain + self.reopen(opts, {'backing': None}) + self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image') + + # Open the 'hd0' image + result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0)) + self.assert_qmp(result, 'return', {}) + + # Reopen the hd1 image setting 'hd0' as its backing image + self.reopen(opts, {'backing': 'hd0'}) + self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0]) + + # Check that nothing has changed since the beginning + self.reopen(hd_opts(0), {'read-only': True}) + self.check_node_graph(original_graph) + + # FIXME: the backing file (hd0) is now a reference so should + # we still be able to change backing.* ? + self.reopen(opts) + + # We can't remove 'hd0' while it's a backing image of 'hd1' + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'") + + # But we can remove both nodes if done in the proper order + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # Reopen a raw image and see the effect of changing the 'offset' option + def test_reopen_raw(self): + opts = {'driver': 'raw', 'node-name': 'hd0', + 'file': { 'driver': 'file', + 'filename': hd_path[0], + 'node-name': 'hd0-file' } } + + # First we create a 2MB raw file, and fill each half with a + # different value + qemu_img('create', '-f', 'raw', hd_path[0], '2M') + qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0]) + qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0]) + + # Open the raw file with QEMU + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Read 1MB from offset 0 + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + + # Reopen the image with a 1MB offset. + # Now the results are different + self.reopen(opts, {'offset': 1024*1024}) + self.run_qemu_io("hd0", "read -P 0xa1 0 1M") + + # Reopen again with the original options. + # We get the original results again + self.reopen(opts) + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + + # Remove the block device + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # Omitting an option should reset it to the default value, but if + # an option cannot be changed it shouldn't be possible to reset it + # to its default value either + def test_reset_default_values(self): + opts = {'driver': 'qcow2', 'node-name': 'hd0', + 'file': { 'driver': 'file', + 'filename': hd_path[0], + 'x-check-cache-dropped': True, # This one can be changed + 'locking': 'off', # This one can NOT be changed + 'node-name': 'hd0-file' } } + + # Open the file with QEMU + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # file.x-check-cache-dropped can be changed... + self.reopen(opts, { 'file.x-check-cache-dropped': False }) + # ...and dropped completely (resetting to the default value) + del opts['file']['x-check-cache-dropped'] + self.reopen(opts) + + # file.locking cannot be changed nor reset to the default value + self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'") + del opts['file']['locking'] + self.reopen(opts, {}, "Option 'locking' can't be reset to its default value") + # But we can reopen it if we maintain its previous value + self.reopen(opts, { 'file.locking': 'off' }) + + # Remove the block device + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # This test modifies the node graph a few times by changing the + # 'backing' option on reopen and verifies that the guest data that + # is read afterwards is consistent with the graph changes. + def test_io_with_graph_changes(self): + opts = [] + + # Open hd0, hd1 and hd2 without any backing image + for i in range(3): + opts.append(hd_opts(i)) + opts[i]['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) + self.assert_qmp(result, 'return', {}) + + # hd0 + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0 1M 1M") + self.run_qemu_io("hd0", "read -P 0 2M 1M") + + # hd1 <- hd0 + self.reopen(opts[0], {'backing': 'hd1'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0 2M 1M") + + # hd1 <- hd0 , hd1 <- hd2 + self.reopen(opts[2], {'backing': 'hd1'}) + + self.run_qemu_io("hd2", "read -P 0 0 1M") + self.run_qemu_io("hd2", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd2", "read -P 0xa2 2M 1M") + + # hd1 <- hd2 <- hd0 + self.reopen(opts[0], {'backing': 'hd2'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # hd2 <- hd0 + self.reopen(opts[2], {'backing': None}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # hd2 <- hd1 <- hd0 + self.reopen(opts[1], {'backing': 'hd2'}) + self.reopen(opts[0], {'backing': 'hd1'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # Illegal operation: hd2 is a child of hd1 + self.reopen(opts[2], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd2' would create a cycle") + + # hd2 <- hd0, hd2 <- hd1 + self.reopen(opts[0], {'backing': 'hd2'}) + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0xa2 2M 1M") + + # More illegal operations + self.reopen(opts[2], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd2' would create a cycle") + self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'") + + # hd1 doesn't have a backing file now + self.reopen(opts[1], {'backing': None}) + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0 2M 1M") + + # Removing the 'backing' option doesn't open the default + # backing file specified in the image (hd0 in this case). + # TODO: change this behavior?? + del opts[1]['backing'] + self.reopen(opts[1]) + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0 2M 1M") + + # This test verifies that we can't change the children of a block + # device during a reopen operation in a way that would create + # cycles in the node graph + def test_graph_cycles(self): + opts = [] + + # Open all three images without backing file + for i in range(3): + opts.append(hd_opts(i)) + opts[i]['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) + self.assert_qmp(result, 'return', {}) + + # hd1 <- hd0, hd1 <- hd2 + self.reopen(opts[0], {'backing': 'hd1'}) + self.reopen(opts[2], {'backing': 'hd1'}) + + # Illegal: hd2 is backed by hd1 + self.reopen(opts[1], {'backing': 'hd2'}, + "Making 'hd2' a backing file of 'hd1' would create a cycle") + + # hd1 <- hd0 <- hd2 + self.reopen(opts[2], {'backing': 'hd0'}) + + # Illegal: hd2 is backed by hd0, which is backed by hd1 + self.reopen(opts[1], {'backing': 'hd2'}, + "Making 'hd2' a backing file of 'hd1' would create a cycle") + + # Illegal: hd1 cannot point to itself + self.reopen(opts[1], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd1' would create a cycle") + + # Remove all backing files + self.reopen(opts[0]) + self.reopen(opts[2]) + + ########################################## + # Add a blkverify node using hd0 and hd1 # + ########################################## + bvopts = {'driver': 'blkverify', + 'node-name': 'bv', + 'test': 'hd0', + 'raw': 'hd1'} + result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts) + self.assert_qmp(result, 'return', {}) + + # blkverify doesn't currently allow reopening. TODO: implement this + self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" + + " does not support reopening files") + + # Illegal: hd0 is a child of the blkverify node + self.reopen(opts[0], {'backing': 'bv'}, + "Making 'bv' a backing file of 'hd0' would create a cycle") + + # Delete the blkverify node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv') + self.assert_qmp(result, 'return', {}) + + # Misc reopen tests with different block drivers + def test_misc_drivers(self): + #################### + ###### quorum ###### + #################### + for i in range(3): + opts = hd_opts(i) + # Open all three images without backing file + opts['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + opts = {'driver': 'quorum', + 'node-name': 'quorum0', + 'children': ['hd0', 'hd1', 'hd2'], + 'vote-threshold': 2} + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Quorum doesn't currently allow reopening. TODO: implement this + self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" + + " does not support reopening files") + + # You can't make quorum0 a backing file of hd0: + # hd0 is already a child of quorum0. + self.reopen(hd_opts(0), {'backing': 'quorum0'}, + "Making 'quorum0' a backing file of 'hd0' would create a cycle") + + # Delete quorum0 + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0') + self.assert_qmp(result, 'return', {}) + + # Delete hd0, hd1 and hd2 + for i in range(3): + result = self.vm.qmp('blockdev-del', conv_keys = True, + node_name = 'hd%d' % i) + self.assert_qmp(result, 'return', {}) + + ###################### + ###### blkdebug ###### + ###################### + opts = {'driver': 'blkdebug', + 'node-name': 'bd', + 'config': '/dev/null', + 'image': hd_opts(0)} + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # blkdebug allows reopening if we keep the same options + self.reopen(opts) + + # but it currently does not allow changes + self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'") + self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'") + self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'") + del opts['config'] + self.reopen(opts, {}, "Option 'config' can't be reset to its default value") + + # Delete the blkdebug node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd') + self.assert_qmp(result, 'return', {}) + + ################## + ###### null ###### + ################## + opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024} + + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # 1 << 30 is the default value, but we cannot change it explicitly + self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'") + + # We cannot change 'size' back to its default value either + del opts['size'] + self.reopen(opts, {}, "Option 'size' can't be reset to its default value") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root') + self.assert_qmp(result, 'return', {}) + + ################## + ###### file ###### + ################## + opts = hd_opts(0) + opts['file']['locking'] = 'on' + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # 'locking' cannot be changed + del opts['file']['locking'] + self.reopen(opts, {'file.locking': 'on'}) + self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {}, "Option 'locking' can't be reset to its default value") + + # Trying to reopen the 'file' node directly does not make a difference + opts = opts['file'] + self.reopen(opts, {'locking': 'on'}) + self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {}, "Option 'locking' can't be reset to its default value") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + ###################### + ###### throttle ###### + ###################### + opts = { 'qom-type': 'throttle-group', 'id': 'group0', + 'props': { 'limits': { 'iops-total': 1000 } } } + result = self.vm.qmp('object-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + opts = { 'qom-type': 'throttle-group', 'id': 'group1', + 'props': { 'limits': { 'iops-total': 2000 } } } + result = self.vm.qmp('object-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Add a throttle filter with group = group0 + opts = { 'driver': 'throttle', 'node-name': 'throttle0', + 'throttle-group': 'group0', 'file': hd_opts(0) } + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # We can reopen it if we keep the same options + self.reopen(opts) + + # These are illegal + self.reopen(opts, {'file': 'hd0'}, "Cannot change the option 'file'") + self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist") + + # But it's possible to change the group to group1 + self.reopen(opts, {'throttle-group': 'group1'}) + + # Now group1 is in use, it cannot be deleted + result = self.vm.qmp('object-del', id = 'group1') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted") + + # Default options, this switches the group back to group0 + self.reopen(opts) + + # So now we cannot delete group0 + result = self.vm.qmp('object-del', id = 'group0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted") + + # But group1 is free this time, and it can be deleted + result = self.vm.qmp('object-del', id = 'group1') + self.assert_qmp(result, 'return', {}) + + # Let's delete the filter node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0') + self.assert_qmp(result, 'return', {}) + + # And we can finally get rid of group0 + result = self.vm.qmp('object-del', id = 'group0') + self.assert_qmp(result, 'return', {}) + + def test_missing_backing_file(self): + # hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Remove hd1 + self.reopen(opts, {'backing': None}) + + # FIXME: this should fail because we're specifying backing.* + # options but we have just removed the backing file + self.reopen(opts) + + # Test what happens if the graph changes due to other operations + # such as block-stream + def test_block_stream(self): + # hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Stream hd1 into hd0 and wait until it's done + result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0') + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive = 'stream0') + + # Now we have only hd0 + self.assertEqual(self.get_node('hd1'), None) + + # FIXME: This should fail, we have backing.* options but + # there's no backing file anymore + self.reopen(opts) + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + +if __name__ == '__main__': + iotests.main(supported_fmts=["qcow2"]) diff --git a/tests/qemu-iotests/220.out b/tests/qemu-iotests/220.out new file mode 100644 index 0000000000..36376bed87 --- /dev/null +++ b/tests/qemu-iotests/220.out @@ -0,0 +1,5 @@ +.......... +---------------------------------------------------------------------- +Ran 10 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0914c922d7..7646f9b621 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -218,3 +218,4 @@ 217 rw auto quick 218 rw auto quick 219 rw auto +220 rw auto quick