From mboxrd@z Thu Jan 1 00:00:00 1970 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 From: Vladimir Sementsov-Ogievskiy Date: Mon, 8 Aug 2016 18:04:59 +0300 Message-Id: <1470668720-211300-9-git-send-email-vsementsov@virtuozzo.com> In-Reply-To: <1470668720-211300-1-git-send-email-vsementsov@virtuozzo.com> References: <1470668720-211300-1-git-send-email-vsementsov@virtuozzo.com> Subject: [Qemu-devel] [PATCH 08/29] qcow2-bitmap: delete bitmap from qcow2 after load List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-block@nongnu.org, qemu-devel@nongnu.org Cc: kwolf@redhat.com, mreitz@redhat.com, armbru@redhat.com, eblake@redhat.com, jsnow@redhat.com, famz@redhat.com, den@openvz.org, stefanha@redhat.com, vsementsov@virtuozzo.com, pbonzini@redhat.com 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; -- 1.8.3.1