Summary
I have a userspace IOUserSCSIParallelInterfaceController dext presenting a virtual disk. It's a software / no-DMA controller — it moves all data with a kernel CPU copy via UserGetDataBuffer; it never does hardware DMA and never reads fBufferIOVMAddr. Buffered I/O works and the disk mounts. The problem is unbuffered / raw-device I/O.
For a data-carrying task, ProcessParallelTask calls PrepareForDMA then GenerateIOVMSegments on the task's IODMACommand, and if the segment count != 1 it fails the task with EIO before ever calling UserProcessParallelTask / UserProcessBundledParallelTasks — so UserGetDataBuffer never gets a chance. I already use UserGetDataBuffer and it works for single-segment (aligned) tasks, but a client buffer that straddles a page boundary produces 2 segments and is rejected at that gate.
Concrete symptom
newfs_apfs writing the container superblock to the raw device from a page-straddling malloc'd buffer:
nx_format:308: failed to write superblock to block 0: 5 - Input/output error
Minimal repro against my raw device: an aligned 4 KiB pwrite succeeds; a 4 KiB pwrite from a buffer straddling a 16 KiB page returns errno 5.
What I think is going on (not sure)
My guess is that the framework expects a DART/IOMMU to coalesce a scattered buffer into one IOVM segment, and my virtual controller doesn't have one (ioreg shows no mapper and no iommu-parent on the node — attached), so GenerateIOVMSegments emits raw physical segments and a straddling buffer stays 2 segments. But I don't know if that's actually the reason, or whether a no-DMA controller is even supposed to go through GenerateIOVMSegments at all — hence the questions below.
Questions
-
For a no-DMA controller that services data via
UserGetDataBuffer, is there a supported way to make the framework deliver a task whose client buffer maps to more than one IOVM segment (relax/skip theGenerateIOVMSegmentssingle-segment gate), so my upcall can run?UserGetDataBufferreturns a fresh contiguous buffer, so the original buffer's segment count shouldn't matter for a CPU-copy controller. -
Is there a characteristic to declare a PIO / no-DMA / software controller so the framework skips segment generation for it?
-
Can a DriverKit controller get macOS to interpose an
IOMapper/DART in front of it (so the buffer is coalesced into one segment) — via a property, a matching personality, or an intermediate provider nub? Or is a hardwareless controller simply not expected to support unbuffered/raw-device I/O?
Environment
- Build: 26A5368g, arm64e
- Full
ioreg -w0 -r -c IOUserSCSIParallelInterfaceControllerattached.
Happy to file a Feedback with a sysdiagnose and the minimal repro.