From patchwork Mon Aug 8 15:05:00 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: 656796 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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 3s7LnF211pz9syB for ; Tue, 9 Aug 2016 01:24:49 +1000 (AEST) Received: from localhost ([::1]:58083 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWmQ3-0001VV-36 for incoming@patchwork.ozlabs.org; Mon, 08 Aug 2016 11:24:47 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42597) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWm7a-00072n-4E 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 1bWm7U-0004X7-Mr for qemu-devel@nongnu.org; Mon, 08 Aug 2016 11:05:39 -0400 Received: from mailhub.sw.ru ([195.214.232.25]:17554 helo=relay.sw.ru) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bWm7T-0004Vy-TQ for qemu-devel@nongnu.org; Mon, 08 Aug 2016 11:05:36 -0400 Received: from kvm.qa.sw.ru. ([10.28.8.145]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id u77NfZcL001857; 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:05:00 +0300 Message-Id: <1470668720-211300-10-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 09/29] qcow2-bitmap: add qcow2_bitmap_store() 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+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" This function stores block dirty bitmap to qcow2. If the bitmap with the same name, size and granularity already exists, it will be rewritten, if the bitmap with the same name exists but granularity or size does not match, an error will be generated. Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/qcow2-bitmap.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 1 + block/qcow2.h | 2 + include/block/block_int.h | 3 + 4 files changed, 303 insertions(+) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index e677c31..43a9bb9 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/cutils.h" #include "block/block_int.h" #include "block/qcow2.h" @@ -93,6 +94,15 @@ static inline void bitmap_table_to_cpu(uint64_t *bitmap_table, size_t size) } } +static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++i) { + cpu_to_be64s(&bitmap_table[i]); + } +} + static inline int calc_dir_entry_size(size_t name_size) { return align_offset(sizeof(QCow2BitmapHeader) + name_size, 8); @@ -537,3 +547,290 @@ BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name, return load_bitmap(bs, h, errp); } + +/* store_bitmap_data() + * Store bitmap to image, filling bitamp table accordingly. + */ +static int store_bitmap_data(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + uint64_t *bitmap_table, uint32_t bitmap_table_size) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + uint64_t sector, dsc; + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + int cl_size = s->cluster_size; + uint8_t *buf = NULL; + uint32_t tb_size = + size_to_clusters(s, + bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); + + BdrvDirtyBitmapIter *dbi; + + if (tb_size != bitmap_table_size) { + return -EINVAL; + } + + memset(bitmap_table, 0, bitmap_table_size * sizeof(bitmap_table[0])); + + dbi = bdrv_dirty_iter_new(bitmap, 0); + buf = g_malloc(cl_size); + dsc = dirty_sectors_in_cluster(s, bitmap); + + while ((sector = bdrv_dirty_iter_next(dbi)) != -1) { + uint64_t cluster = sector / dsc; + sector = cluster * dsc; + uint64_t end = MIN(bm_size, sector + dsc); + uint64_t write_size = + bdrv_dirty_bitmap_serialization_size(bitmap, sector, end - sector); + + int64_t off = qcow2_alloc_clusters(bs, cl_size); + if (off < 0) { + ret = off; + goto finish; + } + bitmap_table[cluster] = off; + + bdrv_dirty_bitmap_serialize_part(bitmap, buf, sector, end); + if (write_size < cl_size) { + memset(buf + write_size, 0, cl_size - write_size); + } + + ret = bdrv_pwrite(bs->file, off, buf, cl_size); + if (ret < 0) { + goto finish; + } + + if (end >= bm_size) { + break; + } + + bdrv_set_dirty_iter(dbi, end); + } + ret = 0; /* writes */ + +finish: + if (ret < 0) { + clear_bitmap_table(bs, bitmap_table, bitmap_table_size); + } + g_free(buf); + bdrv_dirty_iter_free(dbi); + + return ret; +} + +/* store_bitmap() + * Store bitmap to qcow2 and set bitmap_table. bitmap_table itself is not + * stored to qcow2. + */ +static int store_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + uint64_t **bitmap_table, + uint64_t *bitmap_table_offset, + uint32_t *bitmap_table_size) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + + uint64_t *tb; + int64_t tb_offset; + uint32_t tb_size = + size_to_clusters(s, + bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); + + tb = g_try_new(uint64_t, tb_size); + if (tb == NULL) { + return -ENOMEM; + } + + ret = store_bitmap_data(bs, bitmap, tb, tb_size); + if (ret < 0) { + g_free(tb); + return ret; + } + + tb_offset = qcow2_alloc_clusters(bs, tb_size * sizeof(tb[0])); + if (tb_offset < 0) { + ret = tb_offset; + goto fail; + } + + bitmap_table_to_be(tb, tb_size); + ret = bdrv_pwrite(bs->file, tb_offset, tb, tb_size * sizeof(tb[0])); + if (ret < 0) { + goto fail; + } + + if (bitmap_table != NULL) { + bitmap_table_to_cpu(tb, tb_size); + *bitmap_table = tb; + } else { + g_free(tb); + } + if (bitmap_table_offset != NULL) { + *bitmap_table_offset = tb_offset; + } + if (bitmap_table_size != NULL) { + *bitmap_table_size = tb_size; + } + + return 0; + +fail: + clear_bitmap_table(bs, tb, tb_size); + + if (tb_offset > 0) { + qcow2_free_clusters(bs, tb_offset, tb_size, QCOW2_DISCARD_ALWAYS); + } + + g_free(tb); + + return ret; +} + +static int directory_push(BlockDriverState *bs, const char *name, + uint32_t granularity, uint64_t table_offset, uint32_t table_size, + uint32_t flags) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + size_t name_size = strlen(name); + size_t entry_size = calc_dir_entry_size(name_size); + QCow2BitmapHeader *bmh = NULL; + uint64_t new_size = s->bitmap_directory_size + entry_size; + uint8_t *new_dir; + + if (s->nb_bitmaps >= QCOW_MAX_DIRTY_BITMAPS) { + return -EFBIG; + } + + if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) { + return -EINVAL; + } + + new_dir = g_try_malloc(new_size); + if (new_dir == NULL) { + return -ENOMEM; + } + memcpy(new_dir, s->bitmap_directory, s->bitmap_directory_size); + + bmh = (QCow2BitmapHeader *)(new_dir + s->bitmap_directory_size); + bmh->bitmap_table_offset = table_offset; + bmh->bitmap_table_size = table_size; + bmh->flags = flags; + bmh->type = BT_DIRTY_TRACKING_BITMAP; + bmh->granularity_bits = ctz32(granularity); + bmh->name_size = name_size; + bmh->extra_data_size = 0; + memcpy(bmh + 1, name, name_size); + + 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 int directory_set(BlockDriverState *bs, QCow2BitmapHeader *bmh, + uint32_t granularity, uint64_t table_offset, uint32_t table_size, + uint32_t flags) +{ + int ret; + BDRVQcow2State *s = bs->opaque; + uint8_t *new_dir; + + assert((uint8_t *)bmh >= s->bitmap_directory && + (uint8_t *)bmh < s->bitmap_directory + s->bitmap_directory_size); + + new_dir = g_memdup(s->bitmap_directory, s->bitmap_directory_size); + if (new_dir == NULL) { + return -ENOMEM; + } + + bmh = (QCow2BitmapHeader *) + (new_dir + ((uint8_t *)bmh - s->bitmap_directory)); + bmh->bitmap_table_offset = table_offset; + bmh->bitmap_table_size = table_size; + bmh->flags = flags; + bmh->granularity_bits = ctz32(granularity); + + ret = directory_update(bs, new_dir, s->bitmap_directory_size, + s->nb_bitmaps); + if (ret < 0) { + goto fail; + } + + return 0; + +fail: + g_free(new_dir); + + return ret; +} + +void qcow2_bitmap_store(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, Error **errp) +{ + int ret = 0; + QCow2BitmapHeader *bmh; + const char *bm_name = bdrv_dirty_bitmap_name(bitmap); + uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); + uint64_t table_offset; + uint32_t table_size; + uint64_t *bitmap_table; + + /* find/create dirty bitmap */ + bmh = find_bitmap_by_name(bs, bm_name); + if (bmh != NULL) { + if (granularity != (1U << bmh->granularity_bits)) { + error_setg(errp, + "The bitmap with same name (but other granularity) " + "already exists."); + return; + } + + if (bmh->bitmap_table_offset) { + error_setg(errp, + "The bitmap with same name already exists, but was" + "not loaded."); + return; + } + + assert(bmh->bitmap_table_size == 0); + } + + ret = store_bitmap(bs, bitmap, &bitmap_table, &table_offset, &table_size); + if (ret < 0) { + error_setg_errno(errp, ret, "Can't store bitmap table."); + return; + } + + if (bmh == NULL) { + ret = directory_push(bs, bm_name, granularity, table_offset, + table_size, 0); + if (ret < 0) { + error_setg_errno(errp, ret, "Can't create dirty bitmap in qcow2."); + goto fail; + } + } else { + ret = directory_set(bs, bmh, granularity, table_offset, table_size, + bmh->flags); + if (ret < 0) { + error_setg_errno(errp, ret, "Can't update dirty bitmap in qcow2."); + goto fail; + } + } + + g_free(bitmap_table); + return; + +fail: + do_free_bitmap_clusters(bs, table_offset, table_size, bitmap_table); + g_free(bitmap_table); +} diff --git a/block/qcow2.c b/block/qcow2.c index 74dab08..bad7b83 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3424,6 +3424,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_get_specific_info = qcow2_get_specific_info, .bdrv_dirty_bitmap_load = qcow2_bitmap_load, + .bdrv_dirty_bitmap_store = qcow2_bitmap_store, .bdrv_save_vmstate = qcow2_save_vmstate, .bdrv_load_vmstate = qcow2_load_vmstate, diff --git a/block/qcow2.h b/block/qcow2.h index 573fc36..87b0a32 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -610,6 +610,8 @@ int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp); BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name, Error **errp); +void qcow2_bitmap_store(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + Error **errp); /* qcow2-cache.c functions */ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); diff --git a/include/block/block_int.h b/include/block/block_int.h index 2c11ad7..ba002f3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -227,6 +227,9 @@ struct BlockDriver { BdrvDirtyBitmap *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs, const char *name, Error **errp); + void (*bdrv_dirty_bitmap_store)(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,