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 4d0ad37aba)

Change-Id: Ib3ebcd4954c260e75211205b8ec59e346b119ce2
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2747
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
Augustin Cavalier 2020-05-19 00:33:06 -04:00 committed by waddlesplash
parent ec8c6f2da7
commit 00fcefa40e

View File

@ -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)