From patchwork Tue Mar 5 23:43:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Snow X-Patchwork-Id: 10840217 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E1B7E1669 for ; Tue, 5 Mar 2019 23:45:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CF88A2C4C2 for ; Tue, 5 Mar 2019 23:45:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C3AAE2D019; Tue, 5 Mar 2019 23:45:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3E7972C4C2 for ; Tue, 5 Mar 2019 23:45:32 +0000 (UTC) Received: from localhost ([127.0.0.1]:51070 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h1JkZ-0000EW-J2 for patchwork-qemu-devel@patchwork.kernel.org; Tue, 05 Mar 2019 18:45:31 -0500 Received: from eggs.gnu.org ([209.51.188.92]:40430) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h1Jj1-0007NF-Jd for qemu-devel@nongnu.org; Tue, 05 Mar 2019 18:43:56 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1h1Jiy-0005pc-UO for qemu-devel@nongnu.org; Tue, 05 Mar 2019 18:43:54 -0500 Received: from mx1.redhat.com ([209.132.183.28]:48714) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1h1Jir-0005i9-0i; Tue, 05 Mar 2019 18:43:45 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3ED1B3082E4B; Tue, 5 Mar 2019 23:43:44 +0000 (UTC) Received: from probe.bos.redhat.com (dhcp-17-232.bos.redhat.com [10.18.17.232]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7947660C70; Tue, 5 Mar 2019 23:43:43 +0000 (UTC) From: John Snow To: qemu-devel@nongnu.org Date: Tue, 5 Mar 2019 18:43:36 -0500 Message-Id: <20190305234337.18353-5-jsnow@redhat.com> In-Reply-To: <20190305234337.18353-1-jsnow@redhat.com> References: <20190305234337.18353-1-jsnow@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Tue, 05 Mar 2019 23:43:44 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 4/5] block/qcow2-bitmap: Allow resizes with persistent bitmaps 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 , vsementsov@virtuozzo.com, qemu-block@nongnu.org, Max Reitz , John Snow Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Since we now load all bitmaps into memory anyway, we can just truncate them in-memory and then flush them back to disk. Just in case, we will still check and enforce that this shortcut is valid -- i.e. that any bitmap described on-disk is indeed in-memory and can be modified. If there are any inconsistent bitmaps, we refuse to allow the truncate as we do not actually load these bitmaps into memory, and it isn't safe or reasonable to attempt to truncate corrupted data. Signed-off-by: John Snow --- block/qcow2.h | 1 + block/qcow2-bitmap.c | 42 ++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 27 ++++++++++++++++++++------- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/block/qcow2.h b/block/qcow2.h index 4c1fdfcbec..b9227a72cc 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -689,6 +689,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); void qcow2_flush_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 9373055d3b..24f280bccc 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1167,6 +1167,48 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp); } +/* Checks to see if it's safe to resize bitmaps */ +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + Qcow2BitmapList *bm_list; + Qcow2Bitmap *bm; + int ret = 0; + + if (s->nb_bitmaps == 0) { + return 0; + } + + bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, + s->bitmap_directory_size, false, errp); + if (bm_list == NULL) { + return -EINVAL; + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); + if (bitmap == NULL) { + /* We rely on all bitmaps being in-memory to be able to resize them, + * Otherwise, we'd need to resize them on disk explicitly */ + error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that " + "were not loaded into memory"); + ret = -ENOTSUP; + goto out; + } + + /* The checks against readonly and busy are redundant, but certainly + * do no harm. checks against inconsistent are crucial: */ + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { + ret = -ENOTSUP; + goto out; + } + } + +out: + bitmap_list_free(bm_list); + return ret; +} + /* store_bitmap_data() * Store bitmap to image, filling bitmap table accordingly. */ diff --git a/block/qcow2.c b/block/qcow2.c index 7fb2730f09..6fd9252494 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3472,7 +3472,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, { BDRVQcow2State *s = bs->opaque; uint64_t old_length; - int64_t new_l1_size; + int64_t new_l1_size, offset_be; int ret; QDict *options; @@ -3498,10 +3498,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, goto fail; } - /* cannot proceed if image has bitmaps */ - if (s->nb_bitmaps) { - /* TODO: resize bitmaps in the image */ - error_setg(errp, "Can't resize an image which has bitmaps"); + /* Only certain persistent bitmaps can be resized: */ + if (qcow2_truncate_bitmaps_check(bs, errp)) { ret = -ENOTSUP; goto fail; } @@ -3702,9 +3700,9 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bs->total_sectors = offset / BDRV_SECTOR_SIZE; /* write updated header.size */ - offset = cpu_to_be64(offset); + offset_be = cpu_to_be64(offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), - &offset, sizeof(uint64_t)); + &offset_be, sizeof(uint64_t)); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); goto fail; @@ -3719,6 +3717,21 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, if (ret < 0) { goto fail; } + + /* Flush bitmaps */ + if (s->nb_bitmaps) { + Error *local_err = NULL; + + /* Usually, bitmaps get resized after this call. + * Force it earlier so we can make the metadata consistent. */ + bdrv_dirty_bitmap_truncate(bs, offset); + qcow2_flush_persistent_dirty_bitmaps(bs, &local_err); + if (local_err) { + ret = -EINVAL; + goto fail; + } + } + ret = 0; fail: qemu_co_mutex_unlock(&s->lock);