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.)
This commit is contained in:
Augustin Cavalier 2020-05-19 00:30:59 -04:00
parent 16a59954e7
commit 8eb950cd64

View File

@ -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;
}