a83cc2
From 6773549977d94c504ec76aed67506ae85adff973 Mon Sep 17 00:00:00 2001
a83cc2
From: Paolo Bonzini <pbonzini@redhat.com>
a83cc2
Date: Fri, 16 Jul 2021 16:51:33 -0400
a83cc2
Subject: [PATCH 17/43] block: add max_hw_transfer to BlockLimits
a83cc2
a83cc2
RH-Author: Miroslav Rezanina <mrezanin@redhat.com>
a83cc2
RH-Bugzilla: 1957194
a83cc2
a83cc2
For block host devices, I/O can happen through either the kernel file
a83cc2
descriptor I/O system calls (preadv/pwritev, io_submit, io_uring)
a83cc2
or the SCSI passthrough ioctl SG_IO.
a83cc2
a83cc2
In the latter case, the size of each transfer can be limited by the
a83cc2
HBA, while for file descriptor I/O the kernel is able to split and
a83cc2
merge I/O in smaller pieces as needed.  Applying the HBA limits to
a83cc2
file descriptor I/O results in more system calls and suboptimal
a83cc2
performance, so this patch splits the max_transfer limit in two:
a83cc2
max_transfer remains valid and is used in general, while max_hw_transfer
a83cc2
is limited to the maximum hardware size.  max_hw_transfer can then be
a83cc2
included by the scsi-generic driver in the block limits page, to ensure
a83cc2
that the stricter hardware limit is used.
a83cc2
a83cc2
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
a83cc2
(cherry picked from commit 24b36e9813ec15da7db62e3b3621730710c5f020)
a83cc2
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
a83cc2
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
a83cc2
---
a83cc2
 block/block-backend.c          | 13 +++++++++++++
a83cc2
 block/file-posix.c             |  2 +-
a83cc2
 block/io.c                     |  2 ++
a83cc2
 hw/scsi/scsi-generic.c         |  2 +-
a83cc2
 include/block/block_int.h      |  7 +++++++
a83cc2
 include/sysemu/block-backend.h |  1 +
a83cc2
 6 files changed, 25 insertions(+), 2 deletions(-)
a83cc2
a83cc2
diff --git a/block/block-backend.c b/block/block-backend.c
a83cc2
index 136cc602c5..b5f5b4b048 100644
a83cc2
--- a/block/block-backend.c
a83cc2
+++ b/block/block-backend.c
a83cc2
@@ -1939,6 +1939,19 @@ uint32_t blk_get_request_alignment(BlockBackend *blk)
a83cc2
     return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE;
a83cc2
 }
a83cc2
 
a83cc2
+/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */
a83cc2
+uint64_t blk_get_max_hw_transfer(BlockBackend *blk)
a83cc2
+{
a83cc2
+    BlockDriverState *bs = blk_bs(blk);
a83cc2
+    uint64_t max = INT_MAX;
a83cc2
+
a83cc2
+    if (bs) {
a83cc2
+        max = MIN_NON_ZERO(max, bs->bl.max_hw_transfer);
a83cc2
+        max = MIN_NON_ZERO(max, bs->bl.max_transfer);
a83cc2
+    }
a83cc2
+    return ROUND_DOWN(max, blk_get_request_alignment(blk));
a83cc2
+}
a83cc2
+
a83cc2
 /* Returns the maximum transfer length, in bytes; guaranteed nonzero */
a83cc2
 uint32_t blk_get_max_transfer(BlockBackend *blk)
a83cc2
 {
a83cc2
diff --git a/block/file-posix.c b/block/file-posix.c
a83cc2
index 1a6c799e19..44325a635d 100644
a83cc2
--- a/block/file-posix.c
a83cc2
+++ b/block/file-posix.c
a83cc2
@@ -1258,7 +1258,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
a83cc2
         int ret = sg_get_max_transfer_length(s->fd);
a83cc2
 
a83cc2
         if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
a83cc2
-            bs->bl.max_transfer = pow2floor(ret);
a83cc2
+            bs->bl.max_hw_transfer = pow2floor(ret);
a83cc2
         }
a83cc2
 
a83cc2
         ret = sg_get_max_segments(s->fd);
a83cc2
diff --git a/block/io.c b/block/io.c
a83cc2
index ca2dca3007..a4b2e3adf1 100644
a83cc2
--- a/block/io.c
a83cc2
+++ b/block/io.c
a83cc2
@@ -126,6 +126,8 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
a83cc2
 {
a83cc2
     dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
a83cc2
     dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer);
a83cc2
+    dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer,
a83cc2
+                                        src->max_hw_transfer);
a83cc2
     dst->opt_mem_alignment = MAX(dst->opt_mem_alignment,
a83cc2
                                  src->opt_mem_alignment);
a83cc2
     dst->min_mem_alignment = MAX(dst->min_mem_alignment,
a83cc2
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
a83cc2
index 82e1e2ee79..3762dce749 100644
a83cc2
--- a/hw/scsi/scsi-generic.c
a83cc2
+++ b/hw/scsi/scsi-generic.c
a83cc2
@@ -179,7 +179,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
a83cc2
         (r->req.cmd.buf[1] & 0x01)) {
a83cc2
         page = r->req.cmd.buf[2];
a83cc2
         if (page == 0xb0) {
a83cc2
-            uint32_t max_transfer = blk_get_max_transfer(s->conf.blk);
a83cc2
+            uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk);
a83cc2
             uint32_t max_iov = blk_get_max_iov(s->conf.blk);
a83cc2
 
a83cc2
             assert(max_transfer);
a83cc2
diff --git a/include/block/block_int.h b/include/block/block_int.h
a83cc2
index 88e4111939..09d8630ec4 100644
a83cc2
--- a/include/block/block_int.h
a83cc2
+++ b/include/block/block_int.h
a83cc2
@@ -695,6 +695,13 @@ typedef struct BlockLimits {
a83cc2
      * clamped down. */
a83cc2
     uint32_t max_transfer;
a83cc2
 
a83cc2
+    /* Maximal hardware transfer length in bytes.  Applies whenever
a83cc2
+     * transfers to the device bypass the kernel I/O scheduler, for
a83cc2
+     * example with SG_IO.  If larger than max_transfer or if zero,
a83cc2
+     * blk_get_max_hw_transfer will fall back to max_transfer.
a83cc2
+     */
a83cc2
+    uint64_t max_hw_transfer;
a83cc2
+
a83cc2
     /* memory alignment, in bytes so that no bounce buffer is needed */
a83cc2
     size_t min_mem_alignment;
a83cc2
 
a83cc2
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
a83cc2
index 5423e3d9c6..9ac5f7bbd3 100644
a83cc2
--- a/include/sysemu/block-backend.h
a83cc2
+++ b/include/sysemu/block-backend.h
a83cc2
@@ -208,6 +208,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag);
a83cc2
 int blk_get_flags(BlockBackend *blk);
a83cc2
 uint32_t blk_get_request_alignment(BlockBackend *blk);
a83cc2
 uint32_t blk_get_max_transfer(BlockBackend *blk);
a83cc2
+uint64_t blk_get_max_hw_transfer(BlockBackend *blk);
a83cc2
 int blk_get_max_iov(BlockBackend *blk);
a83cc2
 void blk_set_guest_block_size(BlockBackend *blk, int align);
a83cc2
 void *blk_try_blockalign(BlockBackend *blk, size_t size);
a83cc2
-- 
a83cc2
2.27.0
a83cc2