aboutsummaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
Diffstat (limited to 'block')
-rw-r--r--block/mirror.c71
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 |