Hi Kevin!
We finally broke through 20 Gbit/s and only at that speed did another problem become visible. It is not a blocker for us, but we hope you have a solution for it.
Our DEXT moves each task's data between the IOBufferMemoryDescriptor from UserGetDataBuffer and its own shared ring. Tasks are 1 MiB. Both public API paths for that copy are expensive for us, though in different ways.
A: map the descriptor.
GetAddressRange, then memcpy. Every 1 MiB task causes 256 page faults in the DEXT (PROC_PIDTASKINFO pti_faults; the app takes almost none and the peer takes none, no COW faults, no pageins). 2047 KiB tasks cause 514. DEXT CPU per task: 0.62 ms user + 1.34 ms system (READ), 0.47 + 1.00 ms (WRITE). About 2 700 tasks/s with four copy queues, and CPU_Speed_Limit drops to around 60 during the run.
B: kernel copy, no mapping.
One IODMACommand per copy queue, Create(this, 0, {options 0, maxAddressBits 64}, &cmd). Per part: PrepareForDMA(0, hostDescriptor, offset, length, &flags, &count, segments), PerformOperation (OptionWrite for READ data, OptionRead for WRITE data, dmaOffset 0, length, ringOffset, ringDescriptor), CompleteDMA(0). All calls succeed, 582 435 parts, zero error returns, READ data verified (the test target discards WRITE data). The faults are gone, but PerformOperation takes about 1.375 ms of elapsed time per 1 MiB (prepare 10 µs, complete 6 µs) and DEXT CPU per task rises to 0.18 ms user + 3.29 ms system. Median READ at QD8 fell from 22.06 to 15.01 Gbit/s, WRITE from 20.39 to 15.84.
That leaves us with three questions for you:
Is there a supported way to access the task data without faulting every newly mapped page or copying through an intermediate buffer? For example a mapping that is populated up front (kIOMapPrefault is not among the public CreateMapping options and GetAddressRange takes no options).
Is PerformOperation intended for this use case? Its implementation in the KDK appears to allocate an intermediate buffer and copy through it. Is there a way to copy between the two descriptors without that buffer?
This looks like the same gap as in our already reported descriptor issues: every task still materializes a framework owned per task descriptor. Path A faults once per page of it, path B copies through an intermediate buffer. Does the planned software I/O path (task scoped, CPU accessible, multi page descriptor without hardware DMA preparation) cover this? If so, this measurement may be useful as the performance side of those reports.
Thanks,
Torsten