From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: qemu-devel@nongnu.org
Cc: kwolf@redhat.com, vsementsov@virtuozzo.com, stefanha@redhat.com,
pbonzini@redhat.com, den@openvz.org, jsnow@redhat.com
Subject: [Qemu-devel] [PATCH 07/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_store()
Date: Sat, 5 Sep 2015 19:43:49 +0300 [thread overview]
Message-ID: <1441471439-6157-8-git-send-email-vsementsov@virtuozzo.com> (raw)
In-Reply-To: <1441471439-6157-1-git-send-email-vsementsov@virtuozzo.com>
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 genrated.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
block/qcow2-dirty-bitmap.c | 418 +++++++++++++++++++++++++++++++++++++++++++++
block/qcow2.c | 1 +
block/qcow2.h | 2 +
include/block/block_int.h | 2 +
4 files changed, 423 insertions(+)
diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c
index ea50137..39f54e4 100644
--- a/block/qcow2-dirty-bitmap.c
+++ b/block/qcow2-dirty-bitmap.c
@@ -70,6 +70,16 @@ static void bitmap_header_to_cpu(QCowDirtyBitmapHeader *h)
be16_to_cpus(&h->name_size);
}
+static void bitmap_header_to_be(QCowDirtyBitmapHeader *h)
+{
+ cpu_to_be64s(&h->dirty_bitmap_table_offset);
+ cpu_to_be64s(&h->nb_virtual_bits);
+ cpu_to_be32s(&h->dirty_bitmap_table_size);
+ cpu_to_be32s(&h->granularity_bits);
+ cpu_to_be32s(&h->flags);
+ cpu_to_be16s(&h->name_size);
+}
+
static int calc_dir_entry_size(size_t name_size)
{
return align_offset(sizeof(QCowDirtyBitmapHeader) + name_size, 8);
@@ -80,6 +90,17 @@ static int dir_entry_size(QCowDirtyBitmapHeader *h)
return calc_dir_entry_size(h->name_size);
}
+static void directory_to_be(uint8_t *dir, size_t size)
+{
+ uint8_t *end = dir + size;
+ while (dir < end) {
+ QCowDirtyBitmapHeader *h = (QCowDirtyBitmapHeader *)dir;
+ dir += dir_entry_size(h);
+
+ bitmap_header_to_be(h);
+ }
+}
+
static int check_constraints(int cluster_size,
QCowDirtyBitmapHeader *h)
{
@@ -350,3 +371,400 @@ finish:
return bitmap;
}
+
+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 Dirty 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);
+ directory_to_be(dir_be, size);
+
+ /* Allocate space for the new Dirty Bitmap Directory */
+ dir_offset = qcow2_alloc_clusters(bs, size);
+ if (dir_offset < 0) {
+ ret = dir_offset;
+ goto out;
+ }
+
+ /* The Dirty 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_push_entry(BlockDriverState *bs, QCowDirtyBitmapHeader *header)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+ int entry_size = dir_entry_size(header);
+ int64_t new_offset = 0, old_offset = 0;
+ uint64_t new_size = s->dirty_bitmap_directory_size + entry_size, old_size = 0;
+ void *p;
+
+ if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) {
+ return -EINVAL;
+ }
+
+ ret = check_constraints(s->cluster_size, header);
+ if (ret < 0) {
+ return -EINVAL;
+ }
+
+ old_offset = s->dirty_bitmap_directory_offset;
+ old_size = s->dirty_bitmap_directory_size;
+
+ uint8_t *new_dir = g_try_malloc(new_size);
+ if (new_dir == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(new_dir, s->dirty_bitmap_directory, s->dirty_bitmap_directory_size);
+ memcpy(new_dir + s->dirty_bitmap_directory_size, header, entry_size);
+
+ new_offset = directory_write(bs, new_dir, new_size);
+ if (new_offset < 0) {
+ ret = new_offset;
+ goto fail;
+ }
+
+ ret = bdrv_flush(bs);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ s->dirty_bitmap_directory_offset = new_offset;
+ s->dirty_bitmap_directory_size = new_size;
+
+ 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->dirty_bitmap_directory);
+ s->dirty_bitmap_directory = new_dir;
+
+ return 0;
+
+fail:
+ g_free(new_dir);
+ if (new_offset > 0) {
+ qcow2_free_clusters(bs, new_offset, new_size, QCOW2_DISCARD_ALWAYS);
+ s->dirty_bitmap_directory_offset = old_offset;
+ s->dirty_bitmap_directory_size = old_size;
+ }
+
+ p = g_try_realloc(s->dirty_bitmap_directory, s->dirty_bitmap_directory_size);
+ if (p != NULL) {
+ s->dirty_bitmap_directory = p;
+ }
+
+ return ret;
+}
+
+/* store_bitmap()
+ * update Dirty Bitmap Table by storing bitmap to it.
+ * Dirty Bitmap Table entries are assumed to be in big endian format
+ * On the error, the resulting Dirty Bitmap Table is valid for clearing, but
+ * may contain invalid bitmap */
+static int store_bitmap(BlockDriverState *bs,
+ uint64_t *dirty_bitmap_table,
+ uint32_t dirty_bitmap_table_size,
+ const BdrvDirtyBitmap *bitmap)
+{
+ int ret;
+ BDRVQcowState *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 i, tab_size =
+ size_to_clusters(s, bdrv_dirty_bitmap_data_size(bitmap, bm_size));
+
+ if (tab_size > dirty_bitmap_table_size) {
+ return -EINVAL;
+ }
+
+ buf = g_malloc0(cl_size);
+ dsc = dirty_sectors_in_cluster(s, bitmap);
+ for (i = 0, sector = 0; i < tab_size; ++i, sector += dsc) {
+ uint64_t addr = be64_to_cpu(dirty_bitmap_table[i]) & ~511;
+ uint64_t end = MIN(bm_size, sector + dsc);
+ uint64_t write_size = bdrv_dirty_bitmap_data_size(bitmap, end - sector);
+
+ bdrv_dirty_bitmap_serialize_part(bitmap, buf, sector, end);
+
+ if (buffer_is_zero(buf, write_size)) {
+ if (addr) {
+ qcow2_free_clusters(bs, addr, cl_size, QCOW2_DISCARD_ALWAYS);
+ }
+ dirty_bitmap_table[i] = 0;
+ } else {
+ if (!addr) {
+ addr = qcow2_alloc_clusters(bs, cl_size);
+ dirty_bitmap_table[i] = cpu_to_be64(addr);
+ }
+
+ ret = bdrv_pwrite(bs->file, addr, buf, write_size);
+ if (ret < 0) {
+ goto finish;
+ }
+ }
+ }
+ ret = 0;
+
+finish:
+ g_free(buf);
+
+ return ret;
+}
+
+static int64_t alloc_zeroed_clusters(BlockDriverState *bs, uint64_t size)
+{
+ int ret = 0;
+ void *buf = NULL;
+ int64_t offset = qcow2_alloc_clusters(bs, size);
+ if (offset < 0) {
+ return offset;
+ }
+
+ buf = g_try_malloc0(size);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = qcow2_pre_write_overlap_check(bs, 0, offset, size);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = bdrv_pwrite(bs->file, offset, buf, size);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = bdrv_flush(bs);
+ if (ret < 0) {
+ goto out;
+ }
+
+out:
+ g_free(buf);
+
+ if (ret < 0) {
+ qcow2_free_clusters(bs, offset, size, QCOW2_DISCARD_ALWAYS);
+ return ret;
+ }
+
+ return offset;
+}
+
+static int directory_push(BlockDriverState *bs, const char *name,
+ uint64_t size, int granularity)
+{
+ int ret;
+ BDRVQcowState *s = bs->opaque;
+ int sector_granularity = granularity >> BDRV_SECTOR_BITS;
+ size_t name_size = strlen(name);
+ size_t entry_size = calc_dir_entry_size(name_size);
+ QCowDirtyBitmapHeader *entry = g_malloc0(entry_size);
+ int64_t table_offset = 0;
+
+ entry->nb_virtual_bits = size;
+ entry->granularity_bits = ctz32(granularity >> BDRV_SECTOR_BITS);
+ entry->name_size = name_size;
+ memcpy(entry + 1, name, name_size);
+
+ entry->dirty_bitmap_table_size =
+ size_to_clusters(s, (((size - 1) / sector_granularity) >> 3) + 1);
+ table_offset = alloc_zeroed_clusters(bs, entry->dirty_bitmap_table_size *
+ sizeof(uint64_t));
+ if (table_offset < 0) {
+ ret = table_offset;
+ goto out;
+ }
+ entry->dirty_bitmap_table_offset = table_offset;
+
+ ret = directory_push_entry(bs, entry);
+ if (ret < 0) {
+ goto out;
+ }
+
+out:
+ g_free(entry);
+ if (ret < 0 && table_offset > 0) {
+ qcow2_free_clusters(bs, table_offset, entry->dirty_bitmap_table_size *
+ sizeof(uint64_t), QCOW2_DISCARD_ALWAYS);
+ }
+
+ return ret;
+}
+
+static int dirty_bitmaps_push(BDRVQcowState *s, const char *name, uint32_t offset)
+{
+ QCowDirtyBitmap *bm;
+ QCowDirtyBitmap *p;
+
+ printf("dirty bitmaps push\n");
+ p = g_try_renew(QCowDirtyBitmap, s->dirty_bitmaps, s->nb_dirty_bitmaps + 1);
+ if (p == NULL) {
+ return -ENOMEM;
+ }
+ s->dirty_bitmaps = p;
+ s->nb_dirty_bitmaps++;
+
+ bm = s->dirty_bitmaps + s->nb_dirty_bitmaps - 1;
+ bm->name = g_strdup(name);
+ bm->offset = offset;
+
+
+ return 0;
+}
+
+static void dirty_bitmaps_pop(BDRVQcowState *s)
+{
+ QCowDirtyBitmap *p;
+
+ if (s->nb_dirty_bitmaps == 0) {
+ return;
+ }
+
+ p = g_try_renew(QCowDirtyBitmap, s->dirty_bitmaps, s->nb_dirty_bitmaps - 1);
+ if (p != NULL) {
+ s->dirty_bitmaps = p;
+ }
+
+ s->nb_dirty_bitmaps--;
+}
+
+/* if no id is provided, a new one is constructed */
+static int qcow2_dirty_bitmap_create(BlockDriverState *bs, const char *name,
+ uint64_t size, int granularity)
+{
+ int ret;
+ BDRVQcowState *s = bs->opaque;
+
+ if (s->nb_dirty_bitmaps >= QCOW_MAX_DIRTY_BITMAPS) {
+ return -EFBIG;
+ }
+
+ /* Check that the name is unique */
+ if (find_dirty_bitmap_by_name(bs, name) != NULL) {
+ return -EEXIST;
+ }
+
+ ret = dirty_bitmaps_push(s, name, s->dirty_bitmap_directory_size);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = directory_push(bs, name, size, granularity);
+ if (ret < 0) {
+ //dirty_bitmaps_pop(s);
+ return ret;
+ dirty_bitmaps_pop(s);
+ }
+
+ return 0;
+}
+
+int qcow2_dirty_bitmap_store(BlockDriverState *bs, const BdrvDirtyBitmap *bitmap)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret = 0;
+ uint64_t *dirty_bitmap_table;
+ QCowDirtyBitmap *bm;
+ QCowDirtyBitmapHeader *bmh;
+ const char *name = bdrv_dirty_bitmap_name(bitmap);
+ uint64_t size = bdrv_dirty_bitmap_size(bitmap);
+ int granularity = bdrv_dirty_bitmap_granularity(bitmap);
+
+ /* find/create dirty bitmap */
+ bm = find_dirty_bitmap_by_name(bs, name);
+ if (bm == NULL) {
+ ret = qcow2_dirty_bitmap_create(bs, name, size, granularity);
+ if (ret < 0) {
+ return ret;
+ }
+ bm = s->dirty_bitmaps + s->nb_dirty_bitmaps - 1;
+ bmh = bitmap_header(s, bm);
+ } else {
+ bmh = bitmap_header(s, bm);
+
+ if (size != bmh->nb_virtual_bits ||
+ granularity != (BDRV_SECTOR_SIZE << bmh->granularity_bits)) {
+ return -EEXIST;
+ }
+ }
+
+ dirty_bitmap_table = g_try_new(uint64_t, bmh->dirty_bitmap_table_size);
+ if (dirty_bitmap_table == NULL) {
+ return -ENOMEM;
+ }
+ ret = bdrv_pread(bs->file, bmh->dirty_bitmap_table_offset, dirty_bitmap_table,
+ bmh->dirty_bitmap_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ goto finish;
+ }
+
+ ret = store_bitmap(bs, dirty_bitmap_table, bmh->dirty_bitmap_table_size, bitmap);
+ if (ret < 0) {
+ goto finish;
+ }
+
+ ret = bdrv_pwrite(bs->file, bmh->dirty_bitmap_table_offset, dirty_bitmap_table,
+ bmh->dirty_bitmap_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ goto finish;
+ }
+
+finish:
+ g_free(dirty_bitmap_table);
+ return ret;
+}
diff --git a/block/qcow2.c b/block/qcow2.c
index 58ebdd3..e56683a 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2966,6 +2966,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_get_specific_info = qcow2_get_specific_info,
.bdrv_dirty_bitmap_load = qcow2_dirty_bitmap_load,
+ .bdrv_dirty_bitmap_store = qcow2_dirty_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 51d1907..ec42cec 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -612,6 +612,8 @@ BdrvDirtyBitmap *qcow2_dirty_bitmap_load(BlockDriverState *bs_for,
BlockDriverState *bs_file,
const char *name,
Error **errp);
+int qcow2_dirty_bitmap_store(BlockDriverState *bs,
+ const BdrvDirtyBitmap *bitmap);
/* 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 f982adc..c66621e 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -208,6 +208,8 @@ struct BlockDriver {
BlockDriverState *bs_file,
const char *name,
Error **errp);
+ int (*bdrv_dirty_bitmap_store)(BlockDriverState *bs,
+ const BdrvDirtyBitmap *bitmap);
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos);
--
2.1.4
next prev parent reply other threads:[~2015-09-05 16:44 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-05 16:43 [Qemu-devel] [PATCH v3 RFC 0/17] block: persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 01/17] block: fix bdrv_dirty_bitmap_granularity() Vladimir Sementsov-Ogievskiy
2015-09-15 15:36 ` Eric Blake
2015-10-05 22:47 ` John Snow
2015-09-05 16:43 ` [Qemu-devel] [PATCH 02/17] block: add bdrv_dirty_bitmap_size() Vladimir Sementsov-Ogievskiy
2015-09-15 15:37 ` Eric Blake
2015-10-05 22:48 ` John Snow
2015-09-05 16:43 ` [Qemu-devel] [PATCH 03/17] spec: add qcow2-dirty-bitmaps specification Vladimir Sementsov-Ogievskiy
2015-09-05 17:33 ` Vladimir Sementsov-Ogievskiy
2015-10-06 20:22 ` John Snow
2015-10-06 20:33 ` Eric Blake
2015-09-15 16:24 ` Eric Blake
2015-09-16 8:52 ` Vladimir Sementsov-Ogievskiy
2015-10-06 0:09 ` John Snow
2015-10-07 16:47 ` Max Reitz
2015-10-07 19:05 ` Denis V. Lunev
2015-10-08 20:28 ` John Snow
2015-10-08 20:56 ` Denis V. Lunev
2015-10-09 18:14 ` [Qemu-devel] [PATCH " Max Reitz
2015-10-09 17:07 ` [Qemu-devel] [PATCH " Max Reitz
2015-10-09 20:14 ` [Qemu-devel] [Qemu-block] " Eric Blake
2015-09-05 16:43 ` [Qemu-devel] [PATCH 04/17] qcow2: Dirty Bitmaps Ext: structs and consts Vladimir Sementsov-Ogievskiy
2015-10-06 20:12 ` John Snow
2015-10-06 20:16 ` John Snow
2016-02-16 17:04 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 05/17] qcow2-dirty-bitmap: read dirty bitmap directory Vladimir Sementsov-Ogievskiy
2015-10-06 21:27 ` John Snow
2016-02-16 18:51 ` Vladimir Sementsov-Ogievskiy
2016-02-17 15:03 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 06/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_load() Vladimir Sementsov-Ogievskiy
2015-10-06 23:01 ` John Snow
2015-10-07 17:05 ` Eric Blake
2016-02-16 19:04 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` Vladimir Sementsov-Ogievskiy [this message]
2015-09-05 16:43 ` [Qemu-devel] [PATCH 08/17] qcow2: add dirty bitmaps extension Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 09/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_load_check() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 10/17] block: store persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 11/17] block: add bdrv_load_dirty_bitmap() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 12/17] qcow2-dirty-bitmap: add autoclear bit Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 13/17] qemu: command line option for dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 14/17] qcow2-dirty-bitmap: add IN_USE flag Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 15/17] qcow2-dirty-bitmaps: handle store reqursion Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 16/17] iotests: add VM.test_launcn() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 17/17] iotests: test internal persistent dirty bitmap Vladimir Sementsov-Ogievskiy
2015-09-05 16:48 ` [Qemu-devel] [PATCH v3 RFC 0/17] block: persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:51 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:53 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:57 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:03 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:09 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:16 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:25 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:30 ` Vladimir Sementsov-Ogievskiy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1441471439-6157-8-git-send-email-vsementsov@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=den@openvz.org \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).