On Mon, 01/22 23:08, Max Reitz wrote: > @@ -1151,7 +1285,48 @@ static int coroutine_fn > bdrv_mirror_top_preadv(BlockDriverState *bs, > static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, > uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) > { > - return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); > + MirrorOp *op = NULL; > + MirrorBDSOpaque *s = bs->opaque; > + QEMUIOVector bounce_qiov; > + void *bounce_buf; > + int ret = 0; > + bool copy_to_target; > + > + copy_to_target = s->job->ret >= 0 && > + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; > + > + if (copy_to_target) { > + /* The guest might concurrently modify the data to write; but > + * the data on source and destination must match, so we have > + * to use a bounce buffer if we are going to write to the > + * target now. */ > + bounce_buf = qemu_blockalign(bs, bytes); > + iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes);
Quorum doesn't use a bounce buffer, so I think we can get away without it too: a guest concurrently modifying the buffer isn't a concern in practice. > + > + qemu_iovec_init(&bounce_qiov, 1); > + qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); > + qiov = &bounce_qiov; > + > + op = active_write_prepare(s->job, offset, bytes); > + } > + > + ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); > + if (ret < 0) { > + goto out; > + } > + > + if (copy_to_target) { > + do_sync_target_write(s->job, offset, bytes, qiov, flags); > + } > + > +out: > + if (copy_to_target) { > + active_write_settle(op); > + > + qemu_iovec_destroy(&bounce_qiov); > + qemu_vfree(bounce_buf); > + } > + return ret; > } > > static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) Don't you need to update bdrv_mirror_top_pdiscard and bdrv_mirror_top_pwritev too? Fam