From patchwork Tue Mar 15 20:04:17 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Sementsov-Ogievskiy X-Patchwork-Id: 8592231 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 57DC4C0553 for ; Tue, 15 Mar 2016 20:11:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 957FC202F2 for ; Tue, 15 Mar 2016 20:11:36 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AB90420263 for ; Tue, 15 Mar 2016 20:11:35 +0000 (UTC) Received: from localhost ([::1]:51221 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1afvJW-0003Gf-V3 for patchwork-qemu-devel@patchwork.kernel.org; Tue, 15 Mar 2016 16:11:34 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38789) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1afvDa-0007oV-DK for qemu-devel@nongnu.org; Tue, 15 Mar 2016 16:05:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1afvDY-0008Hf-Tz for qemu-devel@nongnu.org; Tue, 15 Mar 2016 16:05:26 -0400 Received: from mailhub.sw.ru ([195.214.232.25]:35111 helo=relay.sw.ru) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1afvDY-0008HF-IB for qemu-devel@nongnu.org; Tue, 15 Mar 2016 16:05:24 -0400 Received: from kvm.sw.ru. ([10.28.8.145]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id u2FK4bCl005992; Tue, 15 Mar 2016 23:05:08 +0300 (MSK) From: Vladimir Sementsov-Ogievskiy To: qemu-devel@nongnu.org Date: Tue, 15 Mar 2016 23:04:17 +0300 Message-Id: <1458072268-53705-12-git-send-email-vsementsov@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1458072268-53705-1-git-send-email-vsementsov@virtuozzo.com> References: <1458072268-53705-1-git-send-email-vsementsov@virtuozzo.com> X-detected-operating-system: by eggs.gnu.org: OpenBSD 3.x X-Received-From: 195.214.232.25 Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, famz@redhat.com, qemu-block@nongnu.org, mreitz@redhat.com, stefanha@redhat.com, pbonzini@redhat.com, den@openvz.org, jsnow@redhat.com Subject: [Qemu-devel] [PATCH 11/22] qcow2: add dirty bitmaps extension X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add dirty bitmap extension as specified in docs/specs/qcow2.txt. Load bitmap headers on open. Handle close and update_header. Handle resize: for now, just block resize if there are dirty bitmaps. Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/qcow2.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/block/qcow2.c b/block/qcow2.c index 20d095b..bda3026 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -62,6 +62,7 @@ typedef struct { #define QCOW2_EXT_MAGIC_END 0 #define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA #define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 +#define QCOW2_EXT_MAGIC_DIRTY_BITMAPS 0x23852875 static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -91,6 +92,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, QCowExtension ext; uint64_t offset; int ret; + Qcow2BitmapHeaderExt bitmaps_ext; #ifdef DEBUG_EXT printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); @@ -161,6 +163,67 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, } break; + case QCOW2_EXT_MAGIC_DIRTY_BITMAPS: + ret = bdrv_pread(bs->file->bs, offset, &bitmaps_ext, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: bitmaps_ext: " + "Could not read ext header"); + return ret; + } + + if (bitmaps_ext.reserved32 != 0) { + error_setg_errno(errp, -ret, "ERROR: bitmaps_ext: " + "Reserved field is not zero."); + return -EINVAL; + } + + be32_to_cpus(&bitmaps_ext.nb_bitmaps); + be64_to_cpus(&bitmaps_ext.bitmap_directory_size); + be64_to_cpus(&bitmaps_ext.bitmap_directory_offset); + + if (bitmaps_ext.nb_bitmaps > QCOW_MAX_DIRTY_BITMAPS) { + error_setg(errp, "ERROR: bitmaps_ext: " + "too many dirty bitmaps"); + return -EINVAL; + } + + if (bitmaps_ext.nb_bitmaps == 0) { + error_setg(errp, "ERROR: bitmaps_ext: " + "found bitmaps extension with zero bitmaps"); + return -EINVAL; + } + + if (bitmaps_ext.bitmap_directory_offset & (s->cluster_size - 1)) { + error_setg(errp, "ERROR: bitmaps_ext: " + "wrong dirty bitmap directory offset"); + return -EINVAL; + } + + if (bitmaps_ext.bitmap_directory_size > + QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) { + error_setg(errp, "ERROR: bitmaps_ext: " + "too large dirty bitmap directory"); + return -EINVAL; + } + + s->nb_bitmaps = bitmaps_ext.nb_bitmaps; + s->bitmap_directory_offset = + bitmaps_ext.bitmap_directory_offset; + s->bitmap_directory_size = + bitmaps_ext.bitmap_directory_size; + + ret = qcow2_read_bitmaps(bs, errp); + if (ret < 0) { + return ret; + } + +#ifdef DEBUG_EXT + printf("Qcow2: Got dirty bitmaps extension:" + " offset=%" PRIu64 " nb_bitmaps=%" PRIu32 "\n", + s->bitmaps_offset, s->nb_bitmaps); +#endif + break; + default: /* unknown magic - save it in case we need to rewrite the header */ { @@ -1178,6 +1241,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); + qcow2_free_bitmaps(bs); qcow2_refcount_close(bs); qemu_vfree(s->l1_table); /* else pre-write overlap checks in cache_destroy may crash */ @@ -1742,6 +1806,7 @@ static void qcow2_close(BlockDriverState *bs) qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); qcow2_free_snapshots(bs); + qcow2_free_bitmaps(bs); } static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) @@ -1939,6 +2004,24 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } + if (s->nb_bitmaps > 0) { + Qcow2BitmapHeaderExt bitmaps_header = { + .nb_bitmaps = cpu_to_be32(s->nb_bitmaps), + .bitmap_directory_size = + cpu_to_be64(s->bitmap_directory_size), + .bitmap_directory_offset = + cpu_to_be64(s->bitmap_directory_offset) + }; + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DIRTY_BITMAPS, + &bitmaps_header, sizeof(bitmaps_header), + buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; + } + /* Keep unknown header extensions */ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); @@ -2466,6 +2549,12 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) return -ENOTSUP; } + /* cannot proceed if image has bitmaps */ + if (s->nb_bitmaps) { + error_report("Can't resize an image which has dirty bitmaps"); + return -ENOTSUP; + } + /* shrinking is currently not supported */ if (offset < bs->total_sectors * 512) { error_report("qcow2 doesn't support shrinking images yet");