From 8eb950cd6421ce20123c99b899d6d7e24dcdddd3 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Tue, 19 May 2020 00:30:59 -0400 Subject: [PATCH] nvme_disk: Fix ior_reset_sgl. The "offset" parameter was not actually an IOV offset, but actually a byte offset across all IOVs... whoops. Somehow, this went unnoticed as most controllers have large enough maximum transfer sizes that we would in practice not hit the limit (even with bs=1M dd tests!) KapiX's controller, as seen in #16049, however, has a maximum transfer size of 64 pages; much smaller than these other controllers, so it did trigger this behavior and exposed the bug. Tested by adding an artificial limit of 2 blocks as the max transfer size (which makes things pretty slow, as you might expect.) --- .../kernel/drivers/disk/nvme/nvme_disk.cpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 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 99d79c9083..eec22b774b 100644 --- a/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/nvme/nvme_disk.cpp @@ -466,12 +466,21 @@ struct nvme_io_request { int32 iovec_count; int32 iovec_i; + uint32 iovec_offset; }; void ior_reset_sgl(nvme_io_request* request, uint32_t offset) { - request->iovec_i = offset; + TRACE("IOR Reset: %" B_PRIu32 "\n", offset); + + int32 i = 0; + while (offset > 0 && request->iovecs[i].size <= offset) { + offset -= request->iovecs[i].size; + i++; + } + request->iovec_i = i; + request->iovec_offset = offset; } @@ -481,13 +490,14 @@ int ior_next_sge(nvme_io_request* request, uint64_t* address, uint32_t* length) if (index < 0 || index > request->iovec_count) return -1; - *address = request->iovecs[index].address; - *length = request->iovecs[index].size; + *address = request->iovecs[index].address + request->iovec_offset; + *length = request->iovecs[index].size - request->iovec_offset; - TRACE("IOV %d: 0x%" B_PRIx64 ", %" B_PRIu32 "\n", request->iovec_i, *address, - *length); + TRACE("IOV %d (+ " B_PRIu32 "): 0x%" B_PRIx64 ", %" B_PRIu32 "\n", + request->iovec_i, request->iovec_offset, *address, *length); request->iovec_i++; + request->iovec_offset = 0; return 0; }