From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: qemu-devel@nongnu.org
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 10/22] qcow2-dirty-bitmap: add qcow2_bitmap_store()
Date: Tue, 15 Mar 2016 23:04:16 +0300 [thread overview]
Message-ID: <1458072268-53705-11-git-send-email-vsementsov@virtuozzo.com> (raw)
In-Reply-To: <1458072268-53705-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 | 443 +++++++++++++++++++++++++++++++++++++++++++++
block/qcow2.c | 1 +
block/qcow2.h | 2 +
include/block/block_int.h | 3 +
4 files changed, 449 insertions(+)
diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c
index c9f7ef1..28ed309 100644
--- a/block/qcow2-dirty-bitmap.c
+++ b/block/qcow2-dirty-bitmap.c
@@ -76,6 +76,15 @@ static void bitmap_header_to_cpu(QCow2BitmapHeader *h)
be32_to_cpus(&h->extra_data_size);
}
+static 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 int calc_dir_entry_size(size_t name_size)
{
return align_offset(sizeof(QCow2BitmapHeader) + name_size, 8);
@@ -86,6 +95,17 @@ static int dir_entry_size(QCow2BitmapHeader *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) {
+ QCow2BitmapHeader *h = (QCow2BitmapHeader *)dir;
+ dir += dir_entry_size(h);
+
+ bitmap_header_to_be(h);
+ }
+}
+
static int check_constraints(QCow2BitmapHeader *h, int cluster_size,
uint64_t disk_size)
{
@@ -320,3 +340,426 @@ BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name,
return load_bitmap(bs, bm, errp);
}
+
+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);
+ 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->bs, 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, QCow2BitmapHeader *header)
+{
+ BDRVQcow2State *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->bitmap_directory_size + entry_size, old_size = 0;
+ void *p;
+ int64_t nb_sectors = bdrv_nb_sectors(bs);
+
+ if (nb_sectors < 0) {
+ return nb_sectors;
+ }
+
+ if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) {
+ return -EINVAL;
+ }
+
+ ret = check_constraints(header, s->cluster_size,
+ nb_sectors << BDRV_SECTOR_BITS);
+ if (ret < 0) {
+ return -EINVAL;
+ }
+
+ old_offset = s->bitmap_directory_offset;
+ old_size = s->bitmap_directory_size;
+
+ uint8_t *new_dir = g_try_malloc(new_size);
+ if (new_dir == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(new_dir, s->bitmap_directory, s->bitmap_directory_size);
+ memcpy(new_dir + s->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->bitmap_directory_offset = new_offset;
+ s->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->bitmap_directory);
+ s->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->bitmap_directory_offset = old_offset;
+ s->bitmap_directory_size = old_size;
+ }
+
+ p = g_try_realloc(s->bitmap_directory, s->bitmap_directory_size);
+ if (p != NULL) {
+ s->bitmap_directory = p;
+ }
+
+ return ret;
+}
+
+/* store_bitmap()
+ * update bitmap table by storing bitmap to it.
+ * Bitmap table entries are assumed to be in big endian format
+ * On the error, the resulting bitmap table is valid for clearing, but
+ * may contain invalid bitmap */
+static int store_bitmap(BlockDriverState *bs, uint64_t *bitmap_table,
+ uint32_t bitmap_table_size,
+ const BdrvDirtyBitmap *bitmap)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+ int cl_size = s->cluster_size;
+ uint32_t i, tab_size = 0;
+ uint64_t *tab;
+ uint32_t j;
+
+ bdrv_dirty_bitmap_prepare_store(bitmap, s->cluster_size, NULL, &tab_size);
+ tab = g_try_malloc(tab_size * sizeof(tab[0]));
+ if (tab == NULL) {
+ return -ENOMEM;
+ }
+
+ ret = bdrv_dirty_bitmap_prepare_store(bitmap, s->cluster_size,
+ tab, &tab_size);
+ if (ret != 0) {
+ g_free(tab);
+ return ret;
+ }
+
+ for (i = 0, j = 0; i < bitmap_table_size; ++i) {
+ if (tab[i] <= 1) {
+ continue;
+ }
+
+ for ( ; j < bitmap_table_size && bitmap_table[j] <= 1; ++j) {
+ ;
+ }
+
+ if (j < bitmap_table_size) {
+ tab[i] = be64_to_cpu(bitmap_table[j]);
+ } else {
+ tab[i] = qcow2_alloc_clusters(bs, cl_size);
+ }
+ }
+
+ for ( ; j < bitmap_table_size; ++j) {
+ uint64_t addr = be64_to_cpu(bitmap_table[j]);
+ if (addr <= 1) {
+ continue;
+ }
+
+ qcow2_free_clusters(bs, addr, cl_size, QCOW2_DISCARD_ALWAYS);
+ bitmap_table[j] = 0;
+ }
+
+ bdrv_dirty_bitmap_store(bitmap, bs->file->bs, tab,
+ bitmap_table_size, s->cluster_size);
+
+ for (i = 0; i < bitmap_table_size; ++i) {
+ bitmap_table[i] = cpu_to_be64(tab[i]);
+ }
+
+ g_free(tab);
+
+ return 0;
+}
+
+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->bs, 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;
+ BDRVQcow2State *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);
+ QCow2BitmapHeader *entry = g_malloc0(entry_size);
+ int64_t table_offset = 0;
+
+ entry->granularity_bits = ctz32(granularity);
+ entry->type = BT_DIRTY_TRACKING_BITMAP;
+ entry->name_size = name_size;
+ memcpy(entry + 1, name, name_size);
+
+ entry->bitmap_table_size =
+ size_to_clusters(s, (((size - 1) / sector_granularity) >> 3) + 1);
+ table_offset = alloc_zeroed_clusters(bs, entry->bitmap_table_size *
+ sizeof(uint64_t));
+ if (table_offset < 0) {
+ ret = table_offset;
+ goto out;
+ }
+ entry->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->bitmap_table_size *
+ sizeof(uint64_t), QCOW2_DISCARD_ALWAYS);
+ }
+
+ return ret;
+}
+
+static int bitmaps_push(BDRVQcow2State *s, const char *name, uint64_t offset)
+{
+ QCow2Bitmap *bm;
+ QCow2Bitmap *p;
+
+ printf("dirty bitmaps push\n");
+ p = g_try_renew(QCow2Bitmap, s->bitmaps, s->nb_bitmaps + 1);
+ if (p == NULL) {
+ return -ENOMEM;
+ }
+ s->bitmaps = p;
+ s->nb_bitmaps++;
+
+ bm = s->bitmaps + s->nb_bitmaps - 1;
+ bm->name = g_strdup(name);
+ bm->offset = offset;
+
+
+ return 0;
+}
+
+static void bitmaps_pop(BDRVQcow2State *s)
+{
+ QCow2Bitmap *p;
+
+ if (s->nb_bitmaps == 0) {
+ return;
+ }
+
+ p = g_try_renew(QCow2Bitmap, s->bitmaps, s->nb_bitmaps - 1);
+ if (p != NULL) {
+ s->bitmaps = p;
+ }
+
+ s->nb_bitmaps--;
+}
+
+/* if no id is provided, a new one is constructed */
+static int qcow2_bitmap_create(BlockDriverState *bs, const char *name,
+ uint64_t size, int granularity)
+{
+ int ret;
+ BDRVQcow2State *s = bs->opaque;
+
+ if (s->nb_bitmaps >= QCOW_MAX_DIRTY_BITMAPS) {
+ return -EFBIG;
+ }
+
+ /* Check that the name is unique */
+ if (find_bitmap_by_name(bs, name) != NULL) {
+ return -EEXIST;
+ }
+
+ ret = bitmaps_push(s, name, s->bitmap_directory_size);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = directory_push(bs, name, size, granularity);
+ if (ret < 0) {
+ bitmaps_pop(s);
+ return ret;
+ }
+
+ return 0;
+}
+
+void qcow2_bitmap_store(BlockDriverState *bs,
+ const BdrvDirtyBitmap *bitmap, Error **errp)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int ret = 0;
+ uint64_t *bitmap_table;
+ QCow2Bitmap *bm;
+ QCow2BitmapHeader *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_bitmap_by_name(bs, name);
+ if (bm == NULL) {
+ ret = qcow2_bitmap_create(bs, name, size, granularity);
+ if (ret < 0) {
+ error_setg_errno(errp, ret, "Can't create dirty bitmap in qcow2.");
+ }
+ bm = s->bitmaps + s->nb_bitmaps - 1;
+ bmh = bitmap_header(s, bm);
+ } else {
+ bmh = bitmap_header(s, bm);
+
+ if (granularity != (1U << bmh->granularity_bits)) {
+ error_setg(errp,
+ "The bitmap with same name (but other granularity) "
+ "already exists.");
+ return;
+ }
+ }
+
+ bitmap_table = g_try_new(uint64_t, bmh->bitmap_table_size);
+ if (bitmap_table == NULL) {
+ error_setg(errp, "No memory.");
+ return;
+ }
+ ret = bdrv_pread(bs->file->bs, bmh->bitmap_table_offset,
+ bitmap_table,
+ bmh->bitmap_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ error_setg_errno(errp, ret, "Can't read dirty bitmap table.");
+ goto finish;
+ }
+
+ ret = store_bitmap(bs, bitmap_table, bmh->bitmap_table_size,
+ bitmap);
+ if (ret < 0) {
+ error_setg_errno(errp, ret, "Can't store bitmap table.");
+ goto finish;
+ }
+
+ ret = bdrv_pwrite(bs->file->bs, bmh->bitmap_table_offset,
+ bitmap_table,
+ bmh->bitmap_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ error_setg_errno(errp, ret, "Can't write dirty bitmap table.");
+ goto finish;
+ }
+
+finish:
+ g_free(bitmap_table);
+}
diff --git a/block/qcow2.c b/block/qcow2.c
index 5f54528..20d095b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3353,6 +3353,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 cc4c776..e4a517c 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -622,6 +622,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, const 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 d0e3db8..7cd05e1 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -218,6 +218,9 @@ struct BlockDriver {
BdrvDirtyBitmap *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs,
const char *name,
Error **errp);
+ void (*bdrv_dirty_bitmap_store)(BlockDriverState *bs,
+ const BdrvDirtyBitmap *bitmap,
+ Error **errp);
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos);
--
1.8.3.1
next prev parent reply other threads:[~2016-03-15 20:05 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-03-15 20:04 [Qemu-devel] [PATCH v5 00/22] qcow2: persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 01/22] block: Add two dirty bitmap getters Vladimir Sementsov-Ogievskiy
2016-03-15 20:08 ` Vladimir Sementsov-Ogievskiy
2016-03-22 16:37 ` Eric Blake
2016-03-22 18:06 ` John Snow
2016-03-15 20:04 ` [Qemu-devel] [PATCH 02/22] block: fix bdrv_dirty_bitmap_granularity signature Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 03/22] iotests: maintain several vms in test Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 04/22] iotests: add default node-name Vladimir Sementsov-Ogievskiy
2016-03-22 18:08 ` John Snow
2016-03-15 20:04 ` [Qemu-devel] [PATCH 05/22] qapi: add md5 checksum of last dirty bitmap level to query-block Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 06/22] hbitmap: load/store Vladimir Sementsov-Ogievskiy
2016-03-21 22:42 ` John Snow
2016-03-22 10:47 ` Vladimir Sementsov-Ogievskiy
2016-03-22 21:49 ` John Snow
2016-03-23 8:22 ` Vladimir Sementsov-Ogievskiy
2016-03-28 20:20 ` John Snow
2016-03-15 20:04 ` [Qemu-devel] [PATCH 07/22] qcow2: Bitmaps extension: structs and consts Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 08/22] qcow2-dirty-bitmap: read dirty bitmap directory Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 09/22] qcow2-dirty-bitmap: add qcow2_bitmap_load() Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` Vladimir Sementsov-Ogievskiy [this message]
2016-03-22 18:49 ` [Qemu-devel] [PATCH 10/22] qcow2-dirty-bitmap: add qcow2_bitmap_store() Eric Blake
2016-03-23 8:25 ` Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 11/22] qcow2: add dirty bitmaps extension Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 12/22] qcow2-dirty-bitmap: add qcow2_bitmap_load_check() Vladimir Sementsov-Ogievskiy
2016-03-22 18:49 ` Eric Blake
2016-03-15 20:04 ` [Qemu-devel] [PATCH 13/22] block: store persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 14/22] block: add bdrv_load_dirty_bitmap() Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 15/22] qcow2-dirty-bitmap: add autoclear bit Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 16/22] qemu: command line option for dirty bitmaps Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 17/22] qcow2-dirty-bitmap: add IN_USE flag Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 18/22] qcow2-dirty-bitmaps: disallow stroing bitmap to other bs Vladimir Sementsov-Ogievskiy
2016-03-22 18:51 ` Eric Blake
2016-03-15 20:04 ` [Qemu-devel] [PATCH 19/22] iotests: add VM.test_launcn() Vladimir Sementsov-Ogievskiy
2016-03-22 18:25 ` Eric Blake
2016-03-15 20:04 ` [Qemu-devel] [PATCH 20/22] iotests: test internal persistent dirty bitmap Vladimir Sementsov-Ogievskiy
2016-03-22 18:27 ` Eric Blake
2016-03-23 8:28 ` Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 21/22] qcow2-dirty-bitmap: add AUTO flag Vladimir Sementsov-Ogievskiy
2016-03-15 20:04 ` [Qemu-devel] [PATCH 22/22] qcow2-dirty-bitmap: add EXTRA_DATA_COMPATIBLE flag Vladimir Sementsov-Ogievskiy
2016-03-22 18:51 ` Eric Blake
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=1458072268-53705-11-git-send-email-vsementsov@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=den@openvz.org \
--cc=famz@redhat.com \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-block@nongnu.org \
--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).