From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:58333) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SfY8C-0005CJ-CJ for qemu-devel@nongnu.org; Fri, 15 Jun 2012 11:08:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SfY83-0005Gu-Eb for qemu-devel@nongnu.org; Fri, 15 Jun 2012 11:08:11 -0400 Received: from mail-pz0-f45.google.com ([209.85.210.45]:49781) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SfY82-0004fx-T9 for qemu-devel@nongnu.org; Fri, 15 Jun 2012 11:08:03 -0400 Received: by mail-pz0-f45.google.com with SMTP id n2so3246681dad.4 for ; Fri, 15 Jun 2012 08:08:02 -0700 (PDT) Sender: Paolo Bonzini From: Paolo Bonzini Date: Fri, 15 Jun 2012 17:05:56 +0200 Message-Id: <1339772759-31004-34-git-send-email-pbonzini@redhat.com> In-Reply-To: <1339772759-31004-1-git-send-email-pbonzini@redhat.com> References: <1339772759-31004-1-git-send-email-pbonzini@redhat.com> Subject: [Qemu-devel] [RFC PATCH 33/36] mirror: perform COW if the cluster size is bigger than the granularity List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: kwolf@redhat.com, stefanha@linux.vnet.ibm.com, lcapitulino@redhat.com 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 --- 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, + §or_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