From 00fcefa40ebc43d6a0e48916b100887a2daa842f Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Tue, 19 May 2020 00:33:06 -0400 Subject: [PATCH] nvme_disk: Avoid doing I/O larger than the maximum size, if possible. libnvme can break up transfers that are too large, but to do so, it allocates one nvme_request struct (of which it has a large, but finite, pool) per segment. Since we are already iterating over "vecs", we might as well cut off transfers after they would otherwise go over the limit. Individual IOVs that are too large are left alone, though; libnvme can still handle this. But at least we no longer try to do all I/O in one go. Tested in a similar manner to the previous commit. (cherry picked from commit 4d0ad37aba376be915192ae142345efd72e00342) Change-Id: Ib3ebcd4954c260e75211205b8ec59e346b119ce2 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2747 Reviewed-by: waddlesplash --- .../kernel/drivers/disk/nvme/nvme_disk.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp index eec22b774b..3a347fbd05 100644 --- a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp @@ -86,6 +86,7 @@ typedef struct { struct nvme_ns* ns; uint64 capacity; uint32 block_size; + uint32 max_io_blocks; status_t media_status; struct qpair_info { @@ -264,6 +265,7 @@ nvme_disk_init_device(void* _info, void** _cookie) // of the block size. restrictions.max_segment_count = (NVME_MAX_SGL_DESCRIPTORS / 2); restrictions.max_transfer_size = cstat.max_xfer_size; + info->max_io_blocks = cstat.max_xfer_size / nsstat.sector_size; err = info->dma_resource.Init(restrictions, B_PAGE_SIZE, buffers, buffers); if (err != 0) { @@ -743,14 +745,25 @@ nvme_disk_io(void* cookie, io_request* request) return status; } + const uint32 max_io_blocks = handle->info->max_io_blocks; int32 remaining = nvme_request.iovec_count; while (remaining > 0) { nvme_request.iovec_count = min_c(remaining, NVME_MAX_SGL_DESCRIPTORS / 2); nvme_request.lba_count = 0; - for (int i = 0; i < nvme_request.iovec_count; i++) - nvme_request.lba_count += (nvme_request.iovecs[i].size / block_size); + for (int i = 0; i < nvme_request.iovec_count; i++) { + int32 vec_lba_count = (nvme_request.iovecs[i].size / block_size); + if (nvme_request.lba_count > 0 + && (nvme_request.lba_count + vec_lba_count) > max_io_blocks) { + // We already have a nonzero length, and adding this vec would + // make us go over (or we already are over.) Stop adding. + nvme_request.iovec_count = i; + break; + } + + nvme_request.lba_count += vec_lba_count; + } status = do_nvme_io_request(handle->info, &nvme_request); if (status != B_OK)