Adds dirty-bitmaps feature to qcow2 format as specified in docs/specs/qcow2.txt
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@parallels.com> --- block/Makefile.objs | 2 +- block/qcow2-dirty-bitmap.c | 514 +++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 26 +++ block/qcow2.h | 48 +++++ include/block/block_int.h | 10 + 5 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 block/qcow2-dirty-bitmap.c diff --git a/block/Makefile.objs b/block/Makefile.objs index 04b0e43..eebd1c9 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -1,5 +1,5 @@ block-obj-y += raw_bsd.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o -block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o +block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-dirty-bitmap.o block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c new file mode 100644 index 0000000..b3d114f --- /dev/null +++ b/block/qcow2-dirty-bitmap.c @@ -0,0 +1,514 @@ +/* + * Dirty bitmpas for the QCOW version 2 format + * + * Copyright (c) 2014-2015 Vladimir Sementsov-Ogievskiy + * + * This file is derived from qcow2-snapshot.c, original copyright: + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/qcow2.h" + +void qcow2_free_dirty_bitmaps(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int i; + + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + g_free(s->dirty_bitmaps[i].name); + } + g_free(s->dirty_bitmaps); + s->dirty_bitmaps = NULL; + s->nb_dirty_bitmaps = 0; +} + +int qcow2_read_dirty_bitmaps(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + QCowDirtyBitmapHeader h; + QCowDirtyBitmap *bm; + int i, name_size; + int64_t offset; + int ret; + + if (!s->nb_dirty_bitmaps) { + s->dirty_bitmaps = NULL; + s->dirty_bitmaps_size = 0; + return 0; + } + + offset = s->dirty_bitmaps_offset; + s->dirty_bitmaps = g_new0(QCowDirtyBitmap, s->nb_dirty_bitmaps); + + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + /* Read statically sized part of the dirty_bitmap header */ + offset = align_offset(offset, 8); + ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { + goto fail; + } + + offset += sizeof(h); + bm = s->dirty_bitmaps + i; + bm->l1_table_offset = be64_to_cpu(h.l1_table_offset); + bm->l1_size = be32_to_cpu(h.l1_size); + bm->bitmap_granularity = be32_to_cpu(h.bitmap_granularity); + bm->bitmap_size = be64_to_cpu(h.bitmap_size); + + name_size = be16_to_cpu(h.name_size); + + /* Read dirty_bitmap name */ + bm->name = g_malloc(name_size + 1); + ret = bdrv_pread(bs->file, offset, bm->name, name_size); + if (ret < 0) { + goto fail; + } + offset += name_size; + bm->name[name_size] = '\0'; + + if (offset - s->dirty_bitmaps_offset > QCOW_MAX_DIRTY_BITMAPS_SIZE) { + ret = -EFBIG; + goto fail; + } + } + + assert(offset - s->dirty_bitmaps_offset <= INT_MAX); + s->dirty_bitmaps_size = offset - s->dirty_bitmaps_offset; + return 0; + +fail: + qcow2_free_dirty_bitmaps(bs); + return ret; +} + +/* add at the end of the file a new list of dirty bitmaps */ +static int qcow2_write_dirty_bitmaps(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + QCowDirtyBitmap *bm; + QCowDirtyBitmapHeader h; + int i, name_size, dirty_bitmaps_size; + struct { + uint32_t nb_dirty_bitmaps; + uint64_t dirty_bitmaps_offset; + } QEMU_PACKED header_data; + int64_t offset, dirty_bitmaps_offset = 0; + int ret; + + /* compute the size of the dirty bitmaps */ + offset = 0; + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + bm = s->dirty_bitmaps + i; + offset = align_offset(offset, 8); + offset += sizeof(h); + offset += strlen(bm->name); + + if (offset > QCOW_MAX_DIRTY_BITMAPS_SIZE) { + ret = -EFBIG; + goto fail; + } + } + + assert(offset <= INT_MAX); + dirty_bitmaps_size = offset; + + /* Allocate space for the new dirty bitmap list */ + dirty_bitmaps_offset = qcow2_alloc_clusters(bs, dirty_bitmaps_size); + offset = dirty_bitmaps_offset; + if (offset < 0) { + ret = offset; + goto fail; + } + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + + /* The dirty bitmap list position has not yet been updated, so these + * clusters must indeed be completely free */ + ret = qcow2_pre_write_overlap_check(bs, 0, offset, dirty_bitmaps_size); + if (ret < 0) { + goto fail; + } + + /* Write all dirty bitmaps to the new list */ + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + bm = s->dirty_bitmaps + i; + memset(&h, 0, sizeof(h)); + h.l1_table_offset = cpu_to_be64(bm->l1_table_offset); + h.l1_size = cpu_to_be32(bm->l1_size); + h.bitmap_granularity = cpu_to_be32(bm->bitmap_granularity); + h.bitmap_size = cpu_to_be64(bm->bitmap_size); + + name_size = strlen(bm->name); + assert(name_size <= UINT16_MAX); + h.name_size = cpu_to_be16(name_size); + offset = align_offset(offset, 8); + + ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { + goto fail; + } + offset += sizeof(h); + + ret = bdrv_pwrite(bs->file, offset, bm->name, name_size); + if (ret < 0) { + goto fail; + } + offset += name_size; + } + + /* + * Update the header to point to the new dirty bitmap table. This requires + * the new table and its refcounts to be stable on disk. + */ + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + + QEMU_BUILD_BUG_ON(offsetof(QCowHeader, dirty_bitmaps_offset) != + offsetof(QCowHeader, nb_dirty_bitmaps) + + sizeof(header_data.nb_dirty_bitmaps)); + + header_data.nb_dirty_bitmaps = cpu_to_be32(s->nb_dirty_bitmaps); + header_data.dirty_bitmaps_offset = cpu_to_be64(dirty_bitmaps_offset); + + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_dirty_bitmaps), + &header_data, sizeof(header_data)); + if (ret < 0) { + goto fail; + } + + /* free the old dirty bitmap table */ + qcow2_free_clusters(bs, s->dirty_bitmaps_offset, s->dirty_bitmaps_size, + QCOW2_DISCARD_ALWAYS); + s->dirty_bitmaps_offset = dirty_bitmaps_offset; + s->dirty_bitmaps_size = dirty_bitmaps_size; + return 0; + +fail: + if (dirty_bitmaps_offset > 0) { + qcow2_free_clusters(bs, dirty_bitmaps_offset, dirty_bitmaps_size, + QCOW2_DISCARD_ALWAYS); + } + return ret; +} + +static int find_dirty_bitmap_by_name(BlockDriverState *bs, + const char *name) +{ + BDRVQcowState *s = bs->opaque; + int i; + + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + if (!strcmp(s->dirty_bitmaps[i].name, name)) { + return i; + } + } + + return -1; +} + +uint8_t *qcow2_dirty_bitmap_load(BlockDriverState *bs, + const char *name, uint64_t size, + int granularity) +{ + BDRVQcowState *s = bs->opaque; + int i, dirty_bitmap_index, ret; + uint64_t offset; + QCowDirtyBitmap *bm; + uint64_t *l1_table; + uint8_t *buf; + + dirty_bitmap_index = find_dirty_bitmap_by_name(bs, name); + if (dirty_bitmap_index < 0) { + return NULL; + } + bm = &s->dirty_bitmaps[dirty_bitmap_index]; + + if (size != bm->bitmap_size || granularity != bm->bitmap_granularity) { + return NULL; + } + + l1_table = g_malloc(bm->l1_size * sizeof(uint64_t)); + ret = bdrv_pread(bs->file, bm->l1_table_offset, l1_table, + bm->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + buf = g_malloc0(bm->l1_size * s->cluster_size); + for (i = 0; i < bm->l1_size; ++i) { + offset = be64_to_cpu(l1_table[i]); + if (!(offset & 1)) { + ret = bdrv_pread(bs->file, offset, buf + i * s->cluster_size, + s->cluster_size); + if (ret < 0) { + goto fail; + } + } + } + + g_free(l1_table); + return buf; + +fail: + g_free(l1_table); + return NULL; +} + +int qcow2_dirty_bitmap_store(BlockDriverState *bs, uint8_t *buf, + const char *name, uint64_t size, + int granularity) +{ + BDRVQcowState *s = bs->opaque; + int cl_size = s->cluster_size; + int i, dirty_bitmap_index, ret = 0, n; + uint64_t *l1_table; + QCowDirtyBitmap *bm; + uint64_t buf_size; + uint8_t *p; + int sector_granularity = granularity >> BDRV_SECTOR_BITS; + + /* find/create dirty bitmap */ + dirty_bitmap_index = find_dirty_bitmap_by_name(bs, name); + if (dirty_bitmap_index >= 0) { + bm = s->dirty_bitmaps + dirty_bitmap_index; + + if (size != bm->bitmap_size || + granularity != bm->bitmap_granularity) { + qcow2_dirty_bitmap_delete(bs, name, NULL); + dirty_bitmap_index = -1; + } + } + if (dirty_bitmap_index < 0) { + qcow2_dirty_bitmap_create(bs, name, size, granularity); + dirty_bitmap_index = s->nb_dirty_bitmaps - 1; + } + bm = s->dirty_bitmaps + dirty_bitmap_index; + + /* read l1 table */ + l1_table = g_malloc(bm->l1_size * sizeof(uint64_t)); + ret = bdrv_pread(bs->file, bm->l1_table_offset, l1_table, + bm->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto finish; + } + + buf_size = (((size - 1) / sector_granularity) >> 3) + 1; + buf_size = align_offset(buf_size, 4); + n = buf_size / cl_size; + p = buf; + for (i = 0; i < bm->l1_size; ++i) { + uint64_t addr = be64_to_cpu(l1_table[i]) & ~511; + int write_size = (i == n ? (buf_size % cl_size) : cl_size); + + if (buffer_is_zero(p, write_size)) { + if (addr) { + qcow2_free_clusters(bs, addr, cl_size, + QCOW2_DISCARD_ALWAYS); + } + l1_table[i] = cpu_to_be64(1); + } else { + if (!addr) { + addr = qcow2_alloc_clusters(bs, cl_size); + l1_table[i] = cpu_to_be64(addr); + } + + ret = bdrv_pwrite(bs->file, addr, p, write_size); + if (ret < 0) { + goto finish; + } + } + + p += cl_size; + } + + ret = bdrv_pwrite(bs->file, bm->l1_table_offset, l1_table, + bm->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto finish; + } + +finish: + g_free(l1_table); + return ret; +} +/* if no id is provided, a new one is constructed */ +int qcow2_dirty_bitmap_create(BlockDriverState *bs, const char *name, + uint64_t size, int granularity) +{ + BDRVQcowState *s = bs->opaque; + QCowDirtyBitmap *new_dirty_bitmap_list = NULL; + QCowDirtyBitmap *old_dirty_bitmap_list = NULL; + QCowDirtyBitmap sn1, *bm = &sn1; + int i, ret; + uint64_t *l1_table = NULL; + int64_t l1_table_offset; + int sector_granularity = granularity >> BDRV_SECTOR_BITS; + + if (s->nb_dirty_bitmaps >= QCOW_MAX_DIRTY_BITMAPS) { + return -EFBIG; + } + + memset(bm, 0, sizeof(*bm)); + + /* Check that the ID is unique */ + if (find_dirty_bitmap_by_name(bs, name) >= 0) { + return -EEXIST; + } + + /* Populate bm with passed data */ + bm->name = g_strdup(name); + bm->bitmap_granularity = granularity; + bm->bitmap_size = size; + + bm->l1_size = + size_to_clusters(s, (((size - 1) / sector_granularity) >> 3) + 1); + l1_table_offset = + qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + if (l1_table_offset < 0) { + ret = l1_table_offset; + goto fail; + } + bm->l1_table_offset = l1_table_offset; + + l1_table = g_try_new(uint64_t, bm->l1_size); + if (l1_table == NULL) { + ret = -ENOMEM; + goto fail; + } + + /* initialize with zero clusters */ + for (i = 0; i < s->l1_size; i++) { + l1_table[i] = cpu_to_be64(1); + } + + ret = qcow2_pre_write_overlap_check(bs, 0, bm->l1_table_offset, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + ret = bdrv_pwrite(bs->file, bm->l1_table_offset, l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + g_free(l1_table); + l1_table = NULL; + + /* Append the new dirty bitmap to the dirty bitmap list */ + new_dirty_bitmap_list = g_new(QCowDirtyBitmap, s->nb_dirty_bitmaps + 1); + if (s->dirty_bitmaps) { + memcpy(new_dirty_bitmap_list, s->dirty_bitmaps, + s->nb_dirty_bitmaps * sizeof(QCowDirtyBitmap)); + old_dirty_bitmap_list = s->dirty_bitmaps; + } + s->dirty_bitmaps = new_dirty_bitmap_list; + s->dirty_bitmaps[s->nb_dirty_bitmaps++] = *bm; + + ret = qcow2_write_dirty_bitmaps(bs); + if (ret < 0) { + g_free(s->dirty_bitmaps); + s->dirty_bitmaps = old_dirty_bitmap_list; + s->nb_dirty_bitmaps--; + goto fail; + } + + g_free(old_dirty_bitmap_list); + + return 0; + +fail: + g_free(bm->name); + g_free(l1_table); + + return ret; +} + +int qcow2_dirty_bitmap_delete(BlockDriverState *bs, + const char *name, + Error **errp) +{ + BDRVQcowState *s = bs->opaque; + QCowDirtyBitmap bm; + int dirty_bitmap_index, ret = 0, i; + uint64_t *l1_table; + + /* Search the dirty_bitmap */ + dirty_bitmap_index = find_dirty_bitmap_by_name(bs, name); + if (dirty_bitmap_index < 0) { + error_setg(errp, "Can't find the dirty bitmap"); + return -ENOENT; + } + bm = s->dirty_bitmaps[dirty_bitmap_index]; + + /* Remove it from the dirty_bitmap list */ + memmove(s->dirty_bitmaps + dirty_bitmap_index, + s->dirty_bitmaps + dirty_bitmap_index + 1, + (s->nb_dirty_bitmaps - dirty_bitmap_index - 1) * sizeof(bm)); + s->nb_dirty_bitmaps--; + ret = qcow2_write_dirty_bitmaps(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to remove dirty bitmap" + " from dirty bitmap list"); + return ret; + } + + /* + * The dirty_bitmap is now unused, clean up. If we fail after this point, we + * won't recover but just leak clusters. + */ + g_free(bm.name); + + /* + * Now decrease the refcounts of clusters referenced by the dirty_bitmap and + * free the L1 table. + */ + l1_table = g_try_new(uint64_t, bm.l1_size); + if (l1_table == NULL) { + ret = -ENOMEM; + goto finish; + } + ret = bdrv_pread(bs->file, bm.l1_table_offset, l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { + goto finish; + } + + for (i = 0; i < bm.l1_size; ++i) { + uint64_t addr = be64_to_cpu(l1_table[i]); + qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS); + } + + qcow2_free_clusters(bs, bm.l1_table_offset, bm.l1_size * sizeof(uint64_t), + QCOW2_DISCARD_ALWAYS); + +finish: + g_free(l1_table); + return ret; +} diff --git a/block/qcow2.c b/block/qcow2.c index e4e690a..6512788 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -570,6 +570,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, be32_to_cpus(&header.refcount_table_clusters); be64_to_cpus(&header.snapshots_offset); be32_to_cpus(&header.nb_snapshots); + be64_to_cpus(&header.dirty_bitmaps_offset); + be32_to_cpus(&header.nb_dirty_bitmaps); if (header.magic != QCOW_MAGIC) { error_setg(errp, "Image is not in qcow2 format"); @@ -892,6 +894,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* Internal bitmaps */ + s->dirty_bitmaps_offset = header.dirty_bitmaps_offset; + s->nb_dirty_bitmaps = header.nb_dirty_bitmaps; + + ret = qcow2_read_dirty_bitmaps(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read dirty bitmaps"); + goto fail; + } + /* Clear unknown autoclear feature bits */ if (!bs->read_only && !(flags & BDRV_O_INCOMING) && s->autoclear_features) { s->autoclear_features = 0; @@ -994,6 +1006,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); + qcow2_free_dirty_bitmaps(bs); qcow2_refcount_close(bs); qemu_vfree(s->l1_table); /* else pre-write overlap checks in cache_destroy may crash */ @@ -1457,6 +1470,7 @@ static void qcow2_close(BlockDriverState *bs) qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); qcow2_free_snapshots(bs); + qcow2_free_dirty_bitmaps(bs); } static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) @@ -1579,6 +1593,8 @@ int qcow2_update_header(BlockDriverState *bs) .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), .nb_snapshots = cpu_to_be32(s->nb_snapshots), .snapshots_offset = cpu_to_be64(s->snapshots_offset), + .nb_dirty_bitmaps = cpu_to_be32(s->nb_dirty_bitmaps), + .dirty_bitmaps_offset = cpu_to_be64(s->dirty_bitmaps_offset), /* Version 3 fields */ .incompatible_features = cpu_to_be64(s->incompatible_features), @@ -2123,6 +2139,12 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) return -ENOTSUP; } + /* cannot proceed if image has dirty_bitmaps */ + if (s->nb_dirty_bitmaps) { + error_report("Can't resize an image which has dirty bitmaps"); + return -ENOTSUP; + } + /* shrinking is currently not supported */ if (offset < bs->total_sectors * 512) { error_report("qcow2 doesn't support shrinking images yet"); @@ -2888,6 +2910,10 @@ BlockDriver bdrv_qcow2 = { .bdrv_get_info = qcow2_get_info, .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_dirty_bitmap_delete = qcow2_dirty_bitmap_delete, + .bdrv_save_vmstate = qcow2_save_vmstate, .bdrv_load_vmstate = qcow2_load_vmstate, diff --git a/block/qcow2.h b/block/qcow2.h index 6e39a1b..45a166d 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -39,6 +39,7 @@ #define QCOW_MAX_CRYPT_CLUSTERS 32 #define QCOW_MAX_SNAPSHOTS 65536 +#define QCOW_MAX_DIRTY_BITMAPS 65536 /* 8 MB refcount table is enough for 2 PB images at 64k cluster size * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ @@ -52,6 +53,8 @@ * space for snapshot names and IDs */ #define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS) +#define QCOW_MAX_DIRTY_BITMAPS_SIZE (1024 * QCOW_MAX_DIRTY_BITMAPS) + /* indicate that the refcount of the referenced cluster is exactly one. */ #define QCOW_OFLAG_COPIED (1ULL << 63) /* indicate that the cluster is compressed (they never have the copied flag) */ @@ -116,6 +119,9 @@ typedef struct QCowHeader { uint32_t refcount_order; uint32_t header_length; + + uint32_t nb_dirty_bitmaps; + uint64_t dirty_bitmaps_offset; } QEMU_PACKED QCowHeader; typedef struct QEMU_PACKED QCowSnapshotHeader { @@ -138,6 +144,19 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { /* name follows */ } QCowSnapshotHeader; +typedef struct QEMU_PACKED QCowDirtyBitmapHeader { + /* header is 8 byte aligned */ + uint64_t l1_table_offset; + + uint32_t l1_size; + uint32_t bitmap_granularity; + + uint64_t bitmap_size; + uint16_t name_size; + + /* name follows */ +} QCowDirtyBitmapHeader; + typedef struct QEMU_PACKED QCowSnapshotExtraData { uint64_t vm_state_size_large; uint64_t disk_size; @@ -156,6 +175,14 @@ typedef struct QCowSnapshot { uint64_t vm_clock_nsec; } QCowSnapshot; +typedef struct QCowDirtyBitmap { + uint64_t l1_table_offset; + uint32_t l1_size; + char *name; + int bitmap_granularity; + uint64_t bitmap_size; +} QCowDirtyBitmap; + struct Qcow2Cache; typedef struct Qcow2Cache Qcow2Cache; @@ -254,6 +281,11 @@ typedef struct BDRVQcowState { unsigned int nb_snapshots; QCowSnapshot *snapshots; + uint64_t dirty_bitmaps_offset; + int dirty_bitmaps_size; + unsigned int nb_dirty_bitmaps; + QCowDirtyBitmap *dirty_bitmaps; + int flags; int qcow_version; bool use_lazy_refcounts; @@ -558,6 +590,22 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); +/* qcow2-dirty-bitmap.c functions */ +int qcow2_dirty_bitmap_store(BlockDriverState *bs, uint8_t *buf, + const char *name, uint64_t size, + int granularity); +uint8_t *qcow2_dirty_bitmap_load(BlockDriverState *bs, + const char *name, uint64_t size, + int granularity); +int qcow2_dirty_bitmap_create(BlockDriverState *bs, const char *name, + uint64_t size, int granularity); +int qcow2_dirty_bitmap_delete(BlockDriverState *bs, + const char *name, + Error **errp); + +void qcow2_free_dirty_bitmaps(BlockDriverState *bs); +int qcow2_read_dirty_bitmaps(BlockDriverState *bs); + /* qcow2-cache.c functions */ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); diff --git a/include/block/block_int.h b/include/block/block_int.h index cb1e4a1..0e3b5b3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -204,6 +204,16 @@ struct BlockDriver { int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs); + int (*bdrv_dirty_bitmap_store)(BlockDriverState *bs, uint8_t *buf, + const char *name, uint64_t size, + int granularity); + uint8_t *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs, + const char *name, uint64_t size, + int granularity); + int (*bdrv_dirty_bitmap_delete)(BlockDriverState *bs, + const char *name, + Error **errp); + int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, -- 1.9.1