9ae3a8
From ddaa3bc746bdf8974337f176e3841b7c27d7253e Mon Sep 17 00:00:00 2001
9ae3a8
From: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Date: Tue, 3 Dec 2013 16:34:41 +0100
9ae3a8
Subject: [PATCH 24/37] block: Align requests in bdrv_co_do_pwritev()
9ae3a8
9ae3a8
Message-id: <1392117622-28812-25-git-send-email-kwolf@redhat.com>
9ae3a8
Patchwork-id: 57189
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH v2 24/37] block: Align requests in bdrv_co_do_pwritev()
9ae3a8
Bugzilla: 748906
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Max Reitz <mreitz@redhat.com>
9ae3a8
9ae3a8
This patch changes bdrv_co_do_pwritev() to actually be what its name
9ae3a8
promises. If requests aren't properly aligned, it performs a RMW.
9ae3a8
9ae3a8
Requests touching the same block are serialised against the RMW request.
9ae3a8
Further optimisation of this is possible by differentiating types of
9ae3a8
requests (concurrent reads should actually be okay here).
9ae3a8
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9ae3a8
Reviewed-by: Benoit Canet <benoit@irqsave.net>
9ae3a8
(cherry picked from commit 3b8242e0ea2a2c201ef3d1bd24080490dae33080)
9ae3a8
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
---
9ae3a8
 block.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
9ae3a8
 1 file changed, 85 insertions(+), 1 deletion(-)
9ae3a8
---
9ae3a8
 block.c |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
9ae3a8
 1 files changed, 85 insertions(+), 1 deletions(-)
9ae3a8
9ae3a8
diff --git a/block.c b/block.c
9ae3a8
index fd37037..3ec3949 100644
9ae3a8
--- a/block.c
9ae3a8
+++ b/block.c
9ae3a8
@@ -3078,6 +3078,12 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
9ae3a8
     BdrvRequestFlags flags)
9ae3a8
 {
9ae3a8
     BdrvTrackedRequest req;
9ae3a8
+    /* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
9ae3a8
+    uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
9ae3a8
+    uint8_t *head_buf = NULL;
9ae3a8
+    uint8_t *tail_buf = NULL;
9ae3a8
+    QEMUIOVector local_qiov;
9ae3a8
+    bool use_local_qiov = false;
9ae3a8
     int ret;
9ae3a8
 
9ae3a8
     if (!bs->drv) {
9ae3a8
@@ -3096,10 +3102,88 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
9ae3a8
         bdrv_io_limits_intercept(bs, true, bytes >> BDRV_SECTOR_BITS);
9ae3a8
     }
9ae3a8
 
9ae3a8
+    /*
9ae3a8
+     * Align write if necessary by performing a read-modify-write cycle.
9ae3a8
+     * Pad qiov with the read parts and be sure to have a tracked request not
9ae3a8
+     * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle.
9ae3a8
+     */
9ae3a8
     tracked_request_begin(&req, bs, offset, bytes, true);
9ae3a8
-    ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, qiov, flags);
9ae3a8
+
9ae3a8
+    if (offset & (align - 1)) {
9ae3a8
+        QEMUIOVector head_qiov;
9ae3a8
+        struct iovec head_iov;
9ae3a8
+
9ae3a8
+        mark_request_serialising(&req, align);
9ae3a8
+        wait_serialising_requests(&req;;
9ae3a8
+
9ae3a8
+        head_buf = qemu_blockalign(bs, align);
9ae3a8
+        head_iov = (struct iovec) {
9ae3a8
+            .iov_base   = head_buf,
9ae3a8
+            .iov_len    = align,
9ae3a8
+        };
9ae3a8
+        qemu_iovec_init_external(&head_qiov, &head_iov, 1);
9ae3a8
+
9ae3a8
+        ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
9ae3a8
+                                  align, &head_qiov, 0);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        qemu_iovec_init(&local_qiov, qiov->niov + 2);
9ae3a8
+        qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
9ae3a8
+        qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
9ae3a8
+        use_local_qiov = true;
9ae3a8
+
9ae3a8
+        bytes += offset & (align - 1);
9ae3a8
+        offset = offset & ~(align - 1);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if ((offset + bytes) & (align - 1)) {
9ae3a8
+        QEMUIOVector tail_qiov;
9ae3a8
+        struct iovec tail_iov;
9ae3a8
+        size_t tail_bytes;
9ae3a8
+
9ae3a8
+        mark_request_serialising(&req, align);
9ae3a8
+        wait_serialising_requests(&req;;
9ae3a8
+
9ae3a8
+        tail_buf = qemu_blockalign(bs, align);
9ae3a8
+        tail_iov = (struct iovec) {
9ae3a8
+            .iov_base   = tail_buf,
9ae3a8
+            .iov_len    = align,
9ae3a8
+        };
9ae3a8
+        qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
9ae3a8
+
9ae3a8
+        ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
9ae3a8
+                                  align, &tail_qiov, 0);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (!use_local_qiov) {
9ae3a8
+            qemu_iovec_init(&local_qiov, qiov->niov + 1);
9ae3a8
+            qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
9ae3a8
+            use_local_qiov = true;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        tail_bytes = (offset + bytes) & (align - 1);
9ae3a8
+        qemu_iovec_add(&local_qiov, tail_buf + tail_bytes, align - tail_bytes);
9ae3a8
+
9ae3a8
+        bytes = ROUND_UP(bytes, align);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = bdrv_aligned_pwritev(bs, &req, offset, bytes,
9ae3a8
+                               use_local_qiov ? &local_qiov : qiov,
9ae3a8
+                               flags);
9ae3a8
+
9ae3a8
+fail:
9ae3a8
     tracked_request_end(&req;;
9ae3a8
 
9ae3a8
+    if (use_local_qiov) {
9ae3a8
+        qemu_iovec_destroy(&local_qiov);
9ae3a8
+        qemu_vfree(head_buf);
9ae3a8
+        qemu_vfree(tail_buf);
9ae3a8
+    }
9ae3a8
+
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8