diff mbox

[09/29] qcow2-bitmap: add qcow2_bitmap_store()

Message ID 1470668720-211300-10-git-send-email-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vladimir Sementsov-Ogievskiy Aug. 8, 2016, 3:05 p.m. UTC
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 <vsementsov@virtuozzo.com>
---
 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 mbox

Patch

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,