diff options
author | Andy Pei <andy.pei@intel.com> | 2021-12-07 09:31:08 +0800 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2021-12-18 11:52:43 -0500 |
commit | a05af290bac55be65e979e007687d4e27cfa7b8d (patch) | |
tree | 35275df7582a11bf214d7094d7bdad05ec15bb03 | |
parent | 815d7498655b8207821d405bc49fa993702f456f (diff) | |
download | seabios-a05af290bac55be65e979e007687d4e27cfa7b8d.tar.gz |
virtio-blk: split large IO according to size_max
if driver reads data larger than VIRTIO_BLK_F_SIZE_MAX,
it will cause some issue to the DMA engine.
So when upper software wants to read data larger than
VIRTIO_BLK_F_SIZE_MAX, virtio-blk driver split one large
request into multiple smaller ones.
Signed-off-by: Andy Pei <andy.pei@intel.com>
Signed-off-by: Ding Limin <dinglimin@cmss.chinamobile.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r-- | src/hw/virtio-blk.c | 35 |
1 files changed, 34 insertions, 1 deletions
diff --git a/src/hw/virtio-blk.c b/src/hw/virtio-blk.c index 82b1d2a8..929ba887 100644 --- a/src/hw/virtio-blk.c +++ b/src/hw/virtio-blk.c @@ -24,6 +24,11 @@ #include "virtio-ring.h" #include "virtio-blk.h" +#define min(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a < _b ? _a : _b; }) + struct virtiodrive_s { struct drive_s drive; struct vring_virtqueue *vq; @@ -82,8 +87,36 @@ virtio_blk_op(struct disk_op_s *op, int write) .length = sizeof(status), }, }; + u32 max_io_size = + vdrive->drive.max_segment_size * vdrive->drive.max_segments; + u16 blk_num_max; + + if (vdrive->drive.blksize != 0 && max_io_size != 0) + blk_num_max = (u16)max_io_size / vdrive->drive.blksize; + else + /* default blk_num_max if hardware doesnot advise a proper value */ + blk_num_max = 8; - virtio_blk_op_one_segment(vdrive, write, sg); + if (op->count <= blk_num_max) { + virtio_blk_op_one_segment(vdrive, write, sg); + } else { + void *p = op->buf_fl; + u16 count = op->count; + + while (count > 0) { + u16 blk_num = min(count, blk_num_max); + sg[1].length = vdrive->drive.blksize * blk_num; + sg[1].addr = p; + virtio_blk_op_one_segment(vdrive, write, sg); + if (status == VIRTIO_BLK_S_OK) { + hdr.sector += blk_num; + p += sg[1].length; + count -= blk_num; + } else { + break; + } + } + } return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; } |