mirror of https://github.com/xqemu/xqemu.git
block: Align requests in bdrv_co_do_pwritev()
This patch changes bdrv_co_do_pwritev() to actually be what its name promises. If requests aren't properly aligned, it performs a RMW. Requests touching the same block are serialised against the RMW request. Further optimisation of this is possible by differentiating types of requests (concurrent reads should actually be okay here). Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Reviewed-by: Benoit Canet <benoit@irqsave.net>
This commit is contained in:
parent
6460440f34
commit
3b8242e0ea
86
block.c
86
block.c
|
@ -3235,6 +3235,12 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
BdrvRequestFlags flags)
|
||||
{
|
||||
BdrvTrackedRequest req;
|
||||
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
|
||||
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
|
||||
uint8_t *head_buf = NULL;
|
||||
uint8_t *tail_buf = NULL;
|
||||
QEMUIOVector local_qiov;
|
||||
bool use_local_qiov = false;
|
||||
int ret;
|
||||
|
||||
if (!bs->drv) {
|
||||
|
@ -3253,10 +3259,88 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
|||
bdrv_io_limits_intercept(bs, bytes >> BDRV_SECTOR_BITS, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Align write if necessary by performing a read-modify-write cycle.
|
||||
* Pad qiov with the read parts and be sure to have a tracked request not
|
||||
* only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle.
|
||||
*/
|
||||
tracked_request_begin(&req, bs, offset, bytes, true);
|
||||
ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, qiov, flags);
|
||||
|
||||
if (offset & (align - 1)) {
|
||||
QEMUIOVector head_qiov;
|
||||
struct iovec head_iov;
|
||||
|
||||
mark_request_serialising(&req, align);
|
||||
wait_serialising_requests(&req);
|
||||
|
||||
head_buf = qemu_blockalign(bs, align);
|
||||
head_iov = (struct iovec) {
|
||||
.iov_base = head_buf,
|
||||
.iov_len = align,
|
||||
};
|
||||
qemu_iovec_init_external(&head_qiov, &head_iov, 1);
|
||||
|
||||
ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
|
||||
align, &head_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_iovec_init(&local_qiov, qiov->niov + 2);
|
||||
qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
|
||||
qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
|
||||
use_local_qiov = true;
|
||||
|
||||
bytes += offset & (align - 1);
|
||||
offset = offset & ~(align - 1);
|
||||
}
|
||||
|
||||
if ((offset + bytes) & (align - 1)) {
|
||||
QEMUIOVector tail_qiov;
|
||||
struct iovec tail_iov;
|
||||
size_t tail_bytes;
|
||||
|
||||
mark_request_serialising(&req, align);
|
||||
wait_serialising_requests(&req);
|
||||
|
||||
tail_buf = qemu_blockalign(bs, align);
|
||||
tail_iov = (struct iovec) {
|
||||
.iov_base = tail_buf,
|
||||
.iov_len = align,
|
||||
};
|
||||
qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
|
||||
|
||||
ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
|
||||
align, &tail_qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!use_local_qiov) {
|
||||
qemu_iovec_init(&local_qiov, qiov->niov + 1);
|
||||
qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
|
||||
use_local_qiov = true;
|
||||
}
|
||||
|
||||
tail_bytes = (offset + bytes) & (align - 1);
|
||||
qemu_iovec_add(&local_qiov, tail_buf + tail_bytes, align - tail_bytes);
|
||||
|
||||
bytes = ROUND_UP(bytes, align);
|
||||
}
|
||||
|
||||
ret = bdrv_aligned_pwritev(bs, &req, offset, bytes,
|
||||
use_local_qiov ? &local_qiov : qiov,
|
||||
flags);
|
||||
|
||||
fail:
|
||||
tracked_request_end(&req);
|
||||
|
||||
if (use_local_qiov) {
|
||||
qemu_iovec_destroy(&local_qiov);
|
||||
qemu_vfree(head_buf);
|
||||
qemu_vfree(tail_buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue