qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 07/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_st


From: Vladimir Sementsov-Ogievskiy
Subject: [Qemu-devel] [PATCH 07/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_store()
Date: Sat, 5 Sep 2015 19:43:49 +0300

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 <address@hidden>
---
 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]