qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [RFC PATCH 33/36] mirror: perform COW if the cluster size i


From: Paolo Bonzini
Subject: [Qemu-devel] [RFC PATCH 33/36] mirror: perform COW if the cluster size is bigger than the granularity
Date: Fri, 15 Jun 2012 17:05:56 +0200

When mirroring runs, the backing files for the target are not yet ready.
However, this means that a copy-on-write operation on the target would
fill the missing sectors with zeros.  Avoid this by always copying a
whole cluster the first time it is touched.

The code keeps a bitmap of clusters that have already been allocated
by the mirroring job, and only does "manual" copy-on-write if the
chunk being copied is zero in the bitmap.

Signed-off-by: Paolo Bonzini <address@hidden>
---
 block/mirror.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 blockdev.c     |    2 --
 2 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index 787b763..fcedd66 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -15,6 +15,7 @@
 #include "blockjob.h"
 #include "block_int.h"
 #include "qemu/ratelimit.h"
+#include "bitmap.h"
 
 enum {
     /*
@@ -36,6 +37,8 @@ typedef struct MirrorBlockJob {
     bool synced;
     bool complete;
     int64_t sector_num;
+    int64_t buf_size;
+    unsigned long *cow_bitmap;
     HBitmapIter hbi;
     void *buf;
 } MirrorBlockJob;
@@ -47,7 +50,7 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
     BlockDriverState *target = s->target;
     QEMUIOVector qiov;
     int ret, nb_sectors;
-    int64_t end;
+    int64_t end, sector_num, cluster_num;
     struct iovec iov;
 
     s->sector_num = hbitmap_iter_next(&s->hbi);
@@ -57,23 +60,41 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
         assert(s->sector_num >= 0);
     }
 
+    /* If we have no backing file yet in the destination, and the cluster size
+     * is very large, we need to do COW ourselves.  The first time a cluster is
+     * copied, copy it entirely.
+     *
+     * Because both BDRV_SECTORS_PER_DIRTY_CHUNK and the cluster size are
+     * powers of two, the number of sectors to copy cannot exceed one cluster.
+     */
+    sector_num = s->sector_num;
+    nb_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+    cluster_num = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
+    if (s->cow_bitmap && !test_bit(cluster_num, s->cow_bitmap)) {
+        bdrv_round_to_clusters(s->target,
+                               sector_num, BDRV_SECTORS_PER_DIRTY_CHUNK,
+                               &sector_num, &nb_sectors);
+        bitmap_set(s->cow_bitmap, sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK,
+                   nb_sectors / BDRV_SECTORS_PER_DIRTY_CHUNK);
+    }
+
     end = s->common.len >> BDRV_SECTOR_BITS;
-    nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
-    trace_mirror_one_iteration(s, s->sector_num);
-    bdrv_reset_dirty(source, s->sector_num, BDRV_SECTORS_PER_DIRTY_CHUNK);
+    nb_sectors = MIN(nb_sectors, end - sector_num);
+    trace_mirror_one_iteration(s, sector_num);
+    bdrv_reset_dirty(source, sector_num, BDRV_SECTORS_PER_DIRTY_CHUNK);
 
     /* Copy the dirty cluster.  */
     iov.iov_base = s->buf;
     iov.iov_len  = nb_sectors * 512;
     qemu_iovec_init_external(&qiov, &iov, 1);
 
-    ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov);
+    ret = bdrv_co_readv(source, sector_num, nb_sectors, &qiov);
     if (ret < 0) {
         *p_action = block_job_error_action(&s->common, source,
                                            s->on_source_error, true, -ret);
         goto fail;
     }
-    ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov);
+    ret = bdrv_co_writev(target, sector_num, nb_sectors, &qiov);
     if (ret < 0) {
         *p_action = block_job_error_action(&s->common, target,
                                            s->on_target_error, false, -ret);
@@ -84,7 +105,7 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
 
 fail:
     /* Try again later.  */
-    bdrv_set_dirty(source, s->sector_num, nb_sectors);
+    bdrv_set_dirty(source, sector_num, nb_sectors);
     return ret;
 }
 
@@ -107,7 +128,7 @@ static void coroutine_fn mirror_run(void *opaque)
     }
 
     end = s->common.len >> BDRV_SECTOR_BITS;
-    s->buf = qemu_blockalign(bs, BLOCK_SIZE);
+    s->buf = qemu_blockalign(bs, s->buf_size);
 
     if (s->mode == MIRROR_SYNC_MODE_FULL || s->mode == MIRROR_SYNC_MODE_TOP) {
         /* First part, loop on the sectors and initialize the dirty bitmap.  */
@@ -211,6 +232,7 @@ static void coroutine_fn mirror_run(void *opaque)
 
 immediate_exit:
     g_free(s->buf);
+    g_free(s->cow_bitmap);
     bdrv_set_dirty_tracking(bs, false);
     bdrv_iostatus_disable(s->target);
     if (s->synced && ret == 0) {
@@ -292,6 +314,9 @@ void mirror_start(BlockDriverState *bs, BlockDriverState 
*target,
                   void *opaque, Error **errp)
 {
     MirrorBlockJob *s;
+    BlockDriverInfo bdi;
+    char backing_filename[1024];
+    int64_t length;
 
     s = block_job_create(&mirror_job_type, bs, speed, cb, opaque, errp);
     if (!s) {
@@ -302,6 +327,23 @@ void mirror_start(BlockDriverState *bs, BlockDriverState 
*target,
     s->on_target_error = on_target_error;
     s->target = target;
     s->mode = mode;
+
+    /* If we have no backing file yet in the destination, we cannot let
+     * the destination do COW.  Instead, we copy sectors around the
+     * dirty data if needed.
+     */
+    s->buf_size = BLOCK_SIZE;
+    bdrv_get_backing_filename(s->target, backing_filename,
+                              sizeof(backing_filename));
+    if (backing_filename[0] && !s->target->backing_hd) {
+        bdrv_get_info(s->target, &bdi);
+        if (s->buf_size < bdi.cluster_size) {
+            s->buf_size = bdi.cluster_size;
+            length = (bdrv_getlength(bs) + BLOCK_SIZE - 1) / BLOCK_SIZE;
+            s->cow_bitmap = bitmap_new(length);
+        }
+    }
+
     bdrv_set_dirty_tracking(bs, true);
     bdrv_set_on_error(s->target, on_target_error, on_target_error);
     bdrv_iostatus_enable(s->target);
diff --git a/blockdev.c b/blockdev.c
index b46a86c..f940e8f 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -927,8 +927,6 @@ void qmp_drive_mirror(const char *device, const char 
*target,
         return;
     }
 
-    /* ### TODO check for cluster size vs. dirty bitmap granularity */
-
     target_bs = bdrv_new("");
     ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
 
-- 
1.7.10.2





reply via email to

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