From patchwork Mon Aug 8 15:04:59 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: 9268587 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 51DF26075A for ; Mon, 8 Aug 2016 15:37:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 42C842837F for ; Mon, 8 Aug 2016 15:37:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 35385283F8; Mon, 8 Aug 2016 15:37:12 +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=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 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.wl.linuxfoundation.org (Postfix) with ESMTPS id 71BBA283F8 for ; Mon, 8 Aug 2016 15:37:11 +0000 (UTC) Received: from localhost ([::1]:58155 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWmc2-0004v7-Fs for patchwork-qemu-devel@patchwork.kernel.org; Mon, 08 Aug 2016 11:37:10 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42637) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWm7a-00072q-4M for qemu-devel@nongnu.org; Mon, 08 Aug 2016 11:05:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bWm7Q-0004Tg-BM for qemu-devel@nongnu.org; Mon, 08 Aug 2016 11:05:40 -0400 Received: from mailhub.sw.ru ([195.214.232.25]:6566 helo=relay.sw.ru) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWm7P-0004PS-LK for qemu-devel@nongnu.org; Mon, 08 Aug 2016 11:05:32 -0400 Received: from kvm.qa.sw.ru. ([10.28.8.145]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id u77NfZcK001857; Mon, 8 Aug 2016 02:41:37 +0300 (MSK) From: Vladimir Sementsov-Ogievskiy To: qemu-block@nongnu.org, qemu-devel@nongnu.org Date: Mon, 8 Aug 2016 18:04:59 +0300 Message-Id: <1470668720-211300-9-git-send-email-vsementsov@virtuozzo.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1470668720-211300-1-git-send-email-vsementsov@virtuozzo.com> References: <1470668720-211300-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 Subject: [Qemu-devel] [PATCH 08/29] qcow2-bitmap: delete bitmap from qcow2 after load X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, famz@redhat.com, armbru@redhat.com, mreitz@redhat.com, stefanha@redhat.com, pbonzini@redhat.com, den@openvz.org, jsnow@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP If we load bitmap for r/w bds, it's data in the image should be considered inconsistent from this point. Therefore it is safe to remove it from the image. Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/qcow2-bitmap.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 280b7bf..e677c31 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -61,6 +61,11 @@ typedef enum BitmapType { BT_DIRTY_TRACKING_BITMAP = 1 } BitmapType; +static inline bool can_write(BlockDriverState *bs) +{ + return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); +} + static inline void bitmap_header_to_cpu(QCow2BitmapHeader *h) { be64_to_cpus(&h->bitmap_table_offset); @@ -70,6 +75,15 @@ static inline void bitmap_header_to_cpu(QCow2BitmapHeader *h) be32_to_cpus(&h->extra_data_size); } +static inline void bitmap_header_to_be(QCow2BitmapHeader *h) +{ + cpu_to_be64s(&h->bitmap_table_offset); + cpu_to_be32s(&h->bitmap_table_size); + cpu_to_be32s(&h->flags); + cpu_to_be16s(&h->name_size); + cpu_to_be32s(&h->extra_data_size); +} + static inline void bitmap_table_to_cpu(uint64_t *bitmap_table, size_t size) { size_t i; @@ -94,6 +108,17 @@ static inline QCow2BitmapHeader *next_dir_entry(QCow2BitmapHeader *entry) return (QCow2BitmapHeader *)((uint8_t *)entry + dir_entry_size(entry)); } +static inline void bitmap_directory_to_be(uint8_t *dir, size_t size) +{ + uint8_t *end = dir + size; + while (dir < end) { + QCow2BitmapHeader *h = (QCow2BitmapHeader *)dir; + dir += dir_entry_size(h); + + bitmap_header_to_be(h); + } +} + /* directory_read * Read bitmaps directory from bs by @offset and @size. Convert it to cpu * format from BE. @@ -174,6 +199,35 @@ static QCow2BitmapHeader *find_bitmap_by_name(BlockDriverState *bs, return NULL; } +static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, + uint32_t bitmap_table_size) +{ + BDRVQcow2State *s = bs->opaque; + int cl_size = s->cluster_size; + int i; + + for (i = 0; i < bitmap_table_size; ++i) { + uint64_t addr = bitmap_table[i]; + if (addr <= 1) { + continue; + } + + qcow2_free_clusters(bs, addr, cl_size, QCOW2_DISCARD_ALWAYS); + bitmap_table[i] = 0; + } +} + +static void do_free_bitmap_clusters(BlockDriverState *bs, + uint64_t table_offset, + uint32_t table_size, + uint64_t *bitmap_table) +{ + clear_bitmap_table(bs, bitmap_table, table_size); + + qcow2_free_clusters(bs, table_offset, table_size * sizeof(uint64_t), + QCOW2_DISCARD_ALWAYS); +} + /* dirty sectors in cluster is a number of sectors in the image, corresponding * to one cluster of bitmap data */ static uint64_t dirty_sectors_in_cluster(const BDRVQcow2State *s, @@ -255,6 +309,165 @@ static int bitmap_table_load(BlockDriverState *bs, QCow2BitmapHeader *bmh, return 0; } +static int update_header_sync(BlockDriverState *bs) +{ + int ret; + + ret = qcow2_update_header(bs); + if (ret < 0) { + return ret; + } + + ret = bdrv_flush(bs); + if (ret < 0) { + return ret; + } + + return 0; +} + +/* write bitmap directory from the state to new allocated clusters */ +static int64_t directory_write(BlockDriverState *bs, const uint8_t *dir, + size_t size) +{ + int ret = 0; + uint8_t *dir_be = NULL; + int64_t dir_offset = 0; + + dir_be = g_try_malloc(size); + if (dir_be == NULL) { + return -ENOMEM; + } + memcpy(dir_be, dir, size); + bitmap_directory_to_be(dir_be, size); + + /* Allocate space for the new bitmap directory */ + dir_offset = qcow2_alloc_clusters(bs, size); + if (dir_offset < 0) { + ret = dir_offset; + goto out; + } + + /* The bitmap directory position has not yet been updated, so these + * clusters must indeed be completely free */ + ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, size); + if (ret < 0) { + goto out; + } + + ret = bdrv_pwrite(bs->file, dir_offset, dir_be, size); + if (ret < 0) { + goto out; + } + +out: + g_free(dir_be); + + if (ret < 0) { + if (dir_offset > 0) { + qcow2_free_clusters(bs, dir_offset, size, QCOW2_DISCARD_ALWAYS); + } + + return ret; + } + + return dir_offset; +} + +static int directory_update(BlockDriverState *bs, uint8_t *new_dir, + size_t new_size, uint32_t new_nb_bitmaps) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + int64_t new_offset = 0; + uint64_t old_offset = s->bitmap_directory_offset; + uint64_t old_size = s->bitmap_directory_size; + uint32_t old_nb_bitmaps = s->nb_bitmaps; + uint64_t old_autocl = s->autoclear_features; + + if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) { + return -EINVAL; + } + + if ((new_size == 0) != (new_nb_bitmaps == 0)) { + return -EINVAL; + } + + if (new_nb_bitmaps > 0) { + new_offset = directory_write(bs, new_dir, new_size); + if (new_offset < 0) { + return new_offset; + } + + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + } + s->bitmap_directory_offset = new_offset; + s->bitmap_directory_size = new_size; + s->nb_bitmaps = new_nb_bitmaps; + + ret = update_header_sync(bs); + if (ret < 0) { + goto fail; + } + + if (old_size) { + qcow2_free_clusters(bs, old_offset, old_size, QCOW2_DISCARD_ALWAYS); + } + + g_free(s->bitmap_directory); + s->bitmap_directory = new_dir; + + return 0; + +fail: + if (new_offset > 0) { + qcow2_free_clusters(bs, new_offset, new_size, QCOW2_DISCARD_ALWAYS); + s->bitmap_directory_offset = old_offset; + s->bitmap_directory_size = old_size; + s->nb_bitmaps = old_nb_bitmaps; + s->autoclear_features = old_autocl; + } + + return ret; +} + +static int directory_del(BlockDriverState *bs, QCow2BitmapHeader *bmh) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + uint8_t *new_dir = NULL; + + size_t sz1 = (uint8_t *)bmh - s->bitmap_directory; + size_t sz2 = dir_entry_size(bmh); + size_t sz3 = s->bitmap_directory_size - sz1 - sz2; + + uint64_t new_size = sz1 + sz3; + + if (new_size > 0) { + new_dir = g_try_malloc(new_size); + if (new_dir == NULL) { + return -ENOMEM; + } + memcpy(new_dir, s->bitmap_directory, sz1); + memcpy(new_dir + sz1, s->bitmap_directory + sz1 + sz2, sz3); + } + + ret = directory_update(bs, new_dir, new_size, s->nb_bitmaps - 1); + if (ret < 0) { + goto fail; + } + + return 0; + +fail: + g_free(new_dir); + + return ret; +} + static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, QCow2BitmapHeader *bmh, Error **errp) { @@ -284,6 +497,20 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, goto fail; } + if (can_write(bs)) { + uint64_t off = bmh->bitmap_table_offset; + uint32_t sz = bmh->bitmap_table_size; + + ret = directory_del(bs, bmh); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Could not free bitmap after read."); + goto fail; + } + + do_free_bitmap_clusters(bs, off, sz, bitmap_table); + } + g_free(name); g_free(bitmap_table); return bitmap;