diff options
Diffstat (limited to 'block/mirror.c')
-rw-r--r-- | block/mirror.c | 198 |
1 files changed, 100 insertions, 98 deletions
diff --git a/block/mirror.c b/block/mirror.c index a6c50ca..f0f2d9d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -620,11 +620,11 @@ static int mirror_exit_common(Job *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockJob *bjob = &s->common; - MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque; + MirrorBDSOpaque *bs_opaque; AioContext *replace_aio_context = NULL; - BlockDriverState *src = s->mirror_top_bs->backing->bs; - BlockDriverState *target_bs = blk_bs(s->target); - BlockDriverState *mirror_top_bs = s->mirror_top_bs; + BlockDriverState *src; + BlockDriverState *target_bs; + BlockDriverState *mirror_top_bs; Error *local_err = NULL; bool abort = job->ret < 0; int ret = 0; @@ -634,6 +634,11 @@ static int mirror_exit_common(Job *job) } s->prepared = true; + mirror_top_bs = s->mirror_top_bs; + bs_opaque = mirror_top_bs->opaque; + src = mirror_top_bs->backing->bs; + target_bs = blk_bs(s->target); + if (bdrv_chain_contains(src, target_bs)) { bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs); } @@ -873,8 +878,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } if (s->bdev_length > base_length) { - ret = blk_truncate(s->target, s->bdev_length, PREALLOC_MODE_OFF, - NULL); + ret = blk_truncate(s->target, s->bdev_length, false, + PREALLOC_MODE_OFF, NULL); if (ret < 0) { goto immediate_exit; } @@ -1181,84 +1186,107 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - QEMUIOVector target_qiov; - uint64_t dirty_offset = offset; - uint64_t dirty_bytes; + int ret; + size_t qiov_offset = 0; + int64_t bitmap_offset, bitmap_end; - if (qiov) { - qemu_iovec_init(&target_qiov, qiov->niov); + 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; } - while (true) { - bool valid_area; - int ret; + 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; - bdrv_dirty_bitmap_lock(job->dirty_bitmap); - dirty_bytes = MIN(offset + bytes - dirty_offset, INT_MAX); - valid_area = bdrv_dirty_bitmap_next_dirty_area(job->dirty_bitmap, - &dirty_offset, - &dirty_bytes); - if (!valid_area) { - bdrv_dirty_bitmap_unlock(job->dirty_bitmap); - break; + if (bytes <= tail) { + /* nothing to do after shrink */ + return; } + bytes -= tail; + } - bdrv_reset_dirty_bitmap_locked(job->dirty_bitmap, - dirty_offset, dirty_bytes); - bdrv_dirty_bitmap_unlock(job->dirty_bitmap); - - job_progress_increase_remaining(&job->common.job, dirty_bytes); - - assert(dirty_offset - offset <= SIZE_MAX); - if (qiov) { - qemu_iovec_reset(&target_qiov); - qemu_iovec_concat(&target_qiov, qiov, - dirty_offset - offset, dirty_bytes); - } + /* + * 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); + } - switch (method) { - case MIRROR_METHOD_COPY: - ret = blk_co_pwritev(job->target, dirty_offset, dirty_bytes, - qiov ? &target_qiov : NULL, flags); - break; + job_progress_increase_remaining(&job->common.job, bytes); - case MIRROR_METHOD_ZERO: - assert(!qiov); - ret = blk_co_pwrite_zeroes(job->target, dirty_offset, dirty_bytes, - flags); - break; + switch (method) { + case MIRROR_METHOD_COPY: + ret = blk_co_pwritev_part(job->target, offset, bytes, + qiov, qiov_offset, flags); + break; - case MIRROR_METHOD_DISCARD: - assert(!qiov); - ret = blk_co_pdiscard(job->target, dirty_offset, dirty_bytes); - break; + case MIRROR_METHOD_ZERO: + assert(!qiov); + ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags); + break; - default: - abort(); - } + case MIRROR_METHOD_DISCARD: + assert(!qiov); + ret = blk_co_pdiscard(job->target, offset, bytes); + break; - if (ret >= 0) { - job_progress_update(&job->common.job, dirty_bytes); - } else { - BlockErrorAction action; + default: + abort(); + } - bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_offset, dirty_bytes); - job->actively_synced = false; + if (ret >= 0) { + job_progress_update(&job->common.job, bytes); + } else { + BlockErrorAction action; - action = mirror_error_action(job, false, -ret); - if (action == BLOCK_ERROR_ACTION_REPORT) { - if (!job->ret) { - job->ret = ret; - } - break; + /* + * 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); + if (action == BLOCK_ERROR_ACTION_REPORT) { + if (!job->ret) { + job->ret = ret; } } - - dirty_offset += dirty_bytes; - } - - if (qiov) { - qemu_iovec_destroy(&target_qiov); } } @@ -1465,15 +1493,6 @@ static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, *nshared = BLK_PERM_ALL; } -static void bdrv_mirror_top_refresh_limits(BlockDriverState *bs, Error **errp) -{ - MirrorBDSOpaque *s = bs->opaque; - - if (s && s->job && s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) { - bs->bl.request_alignment = s->job->granularity; - } -} - /* Dummy node that provides consistent read to its users without requiring it * from its backing file and that allows writes on the backing file chain. */ static BlockDriver bdrv_mirror_top = { @@ -1486,7 +1505,6 @@ static BlockDriver bdrv_mirror_top = { .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, .bdrv_child_perm = bdrv_mirror_top_child_perm, - .bdrv_refresh_limits = bdrv_mirror_top_refresh_limits, }; static BlockJob *mirror_start_job( @@ -1634,29 +1652,13 @@ static BlockJob *mirror_start_job( s->should_complete = true; } - /* - * Must be called before we start tracking writes, but after - * - * ((MirrorBlockJob *) - * ((MirrorBDSOpaque *) - * mirror_top_bs->opaque - * )->job - * )->copy_mode - * - * has the correct value. - * (We start tracking writes as of the following - * bdrv_create_dirty_bitmap() call.) - */ - bdrv_refresh_limits(mirror_top_bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto fail; - } - s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); 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 | |