diff options
Diffstat (limited to 'block/mirror.c')
-rw-r--r-- | block/mirror.c | 71 |
1 files changed, 68 insertions, 3 deletions
diff --git a/block/mirror.c b/block/mirror.c index 351faf9367..11d4d66f43 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1182,14 +1182,67 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, QEMUIOVector *qiov, int flags) { int ret; + size_t qiov_offset = 0; + int64_t bitmap_offset, bitmap_end; - bdrv_reset_dirty_bitmap(job->dirty_bitmap, offset, bytes); + if (!QEMU_IS_ALIGNED(offset, job->granularity) && + bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) + { + /* + * Dirty unaligned padding: ignore it. + * + * Reasoning: + * 1. If we copy it, we can't reset corresponding bit in + * dirty_bitmap as there may be some "dirty" bytes still not + * copied. + * 2. It's already dirty, so skipping it we don't diverge mirror + * progress. + * + * Note, that because of this, guest write may have no contribution + * into mirror converge, but that's not bad, as we have background + * process of mirroring. If under some bad circumstances (high guest + * IO load) background process starve, we will not converge anyway, + * even if each write will contribute, as guest is not guaranteed to + * rewrite the whole disk. + */ + qiov_offset = QEMU_ALIGN_UP(offset, job->granularity) - offset; + if (bytes <= qiov_offset) { + /* nothing to do after shrink */ + return; + } + offset += qiov_offset; + bytes -= qiov_offset; + } + + if (!QEMU_IS_ALIGNED(offset + bytes, job->granularity) && + bdrv_dirty_bitmap_get(job->dirty_bitmap, offset + bytes - 1)) + { + uint64_t tail = (offset + bytes) % job->granularity; + + if (bytes <= tail) { + /* nothing to do after shrink */ + return; + } + bytes -= tail; + } + + /* + * Tails are either clean or shrunk, so for bitmap resetting + * we safely align the range down. + */ + bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); + bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); + if (bitmap_offset < bitmap_end) { + bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset, + bitmap_end - bitmap_offset); + } job_progress_increase_remaining(&job->common.job, bytes); switch (method) { case MIRROR_METHOD_COPY: - ret = blk_co_pwritev(job->target, offset, bytes, qiov, flags); + ret = blk_co_pwritev_part(job->target, offset, bytes, + qiov, qiov_offset, flags); break; case MIRROR_METHOD_ZERO: @@ -1211,7 +1264,16 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, } else { BlockErrorAction action; - bdrv_set_dirty_bitmap(job->dirty_bitmap, offset, bytes); + /* + * We failed, so we should mark dirty the whole area, aligned up. + * Note that we don't care about shrunk tails if any: they were dirty + * at function start, and they must be still dirty, as we've locked + * the region for in-flight op. + */ + bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); + bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); + bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, + bitmap_end - bitmap_offset); job->actively_synced = false; action = mirror_error_action(job, false, -ret); @@ -1618,6 +1680,9 @@ static BlockJob *mirror_start_job( if (!s->dirty_bitmap) { goto fail; } + if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) { + bdrv_disable_dirty_bitmap(s->dirty_bitmap); + } ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | |