@@ -1304,29 +1304,40 @@ static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs,
MirrorBDSOpaque *s = bs->opaque;
int ret = 0;
bool copy_to_target;
+ bool write_to_source;
copy_to_target = s->job->ret >= 0 &&
s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING;
+ /* WRITE_UNCHANGED requests are allocating writes, which in this
+ * case means that we should ensure the target is in sync with the
+ * source (by writing the data to the target). Therefore, if we
+ * do write to the target (only in write-blocking mode), skip
+ * writing the (unchanged) data to the source. */
+ write_to_source = s->job->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING ||
+ !(flags & BDRV_REQ_WRITE_UNCHANGED);
+
if (copy_to_target) {
op = active_write_prepare(s->job, offset, bytes);
}
- switch (method) {
- case MIRROR_METHOD_COPY:
- ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
- break;
+ if (write_to_source) {
+ switch (method) {
+ case MIRROR_METHOD_COPY:
+ ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
+ break;
- case MIRROR_METHOD_ZERO:
- ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
- break;
+ case MIRROR_METHOD_ZERO:
+ ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
+ break;
- case MIRROR_METHOD_DISCARD:
- ret = bdrv_co_pdiscard(bs->backing, offset, bytes);
- break;
+ case MIRROR_METHOD_DISCARD:
+ ret = bdrv_co_pdiscard(bs->backing, offset, bytes);
+ break;
- default:
- abort();
+ default:
+ abort();
+ }
}
if (ret < 0) {
@@ -1403,6 +1414,57 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs,
NULL, 0);
}
+/**
+ * Allocation status is determined by whether source and target are in
+ * sync:
+ * - If they are (dirty bitmap is clean), the data is considered to be
+ * allocated in this layer. Then, return BDRV_BLOCK_RAW so the
+ * request is forwarded to the source.
+ * - Dirty (unsynced) areas are considered unallocated. For those,
+ * return 0.
+ */
+static int coroutine_fn bdrv_mirror_top_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset,
+ int64_t bytes,
+ int64_t *pnum,
+ int64_t *map,
+ BlockDriverState **file)
+{
+ MirrorBDSOpaque *s = bs->opaque;
+ BdrvDirtyBitmapIter *iter;
+ uint64_t dirty_offset, clean_offset;
+ int ret;
+
+ *map = offset;
+ *file = bs->backing->bs;
+
+ iter = bdrv_dirty_iter_new(s->job->dirty_bitmap);
+ bdrv_set_dirty_iter(iter, offset);
+
+ bdrv_dirty_bitmap_lock(s->job->dirty_bitmap);
+ dirty_offset = bdrv_dirty_iter_next(iter);
+ bdrv_dirty_iter_free(iter);
+ if (dirty_offset > offset) {
+ /* Clean area */
+ *pnum = MIN(dirty_offset - offset, bytes);
+ ret = BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
+ goto out;
+ }
+
+ /* Dirty area, find next clean area */
+ clean_offset = bdrv_dirty_bitmap_next_zero(s->job->dirty_bitmap, offset);
+ bdrv_dirty_bitmap_unlock(s->job->dirty_bitmap);
+
+ assert(clean_offset > offset);
+ *pnum = MIN(clean_offset - offset, bytes);
+ ret = 0;
+
+out:
+ bdrv_dirty_bitmap_unlock(s->job->dirty_bitmap);
+ return ret;
+}
+
static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs)
{
if (bs->backing == NULL) {
@@ -1442,7 +1504,7 @@ static BlockDriver bdrv_mirror_top = {
.bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes,
.bdrv_co_pdiscard = bdrv_mirror_top_pdiscard,
.bdrv_co_flush = bdrv_mirror_top_flush,
- .bdrv_co_block_status = bdrv_co_block_status_from_backing,
+ .bdrv_co_block_status = bdrv_mirror_top_block_status,
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
.bdrv_close = bdrv_mirror_top_close,
.bdrv_child_perm = bdrv_mirror_top_child_perm,
This patch adds a .bdrv_co_block_status() implementation for the mirror block job that reports an area as allocated iff source and target are in sync. This allows putting a copy-on-read node on top of a mirror node which automatically copies all data read from the source to the target. To make this perform better, bdrv_mirror_top_do_write() is modified to ignore BDRV_REQ_WRITE_UNCHANGED requests regarding the source, and only write them to the target (in write-blocking mode). Otherwise, using COR would result in all data read from the source that is not in sync with the target to be re-written to the source (which is not the intention of COR). Signed-off-by: Max Reitz <mreitz@redhat.com> --- block/mirror.c | 88 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 13 deletions(-)