Post

Replies

Boosts

Views

Activity

Reply to # IOUserSCSIParallelInterfaceController: `UserGetDataBuffer` returns a zero-filled buffer for some writes
Thanks Kevin. I went through your answer point by point with experiments — here's what I found. it's not necessarily true that pwrite DID return EIO (at least for that log message) I wasn't sure either, so I traced it with dtruss — in my case it really is pwrite(2) returning EIO (fd 3 = the raw device): pwrite(0x3, ..., 0x1000, 0x2000) = 4096 0 pwrite(0x3, "x\0", 0x1000, 0x0) = -1 Err#5 ← the superblock write write_nocancel(0x2, "nx_format:308: failed to write superblock to block 0: 5 - Input/output error\n", 0x4D) lldb shows the failing buffer is a 32-byte-aligned heap pointer, so whether it straddles a page boundary is an allocator lottery — which also explains why diskutil eraseDisk only fails some of the time. the problem is that you haven't properly configured all of the keys required by UserReportHBAConstraints All seven keys are defined (and also republished as node properties via SetProperties, per FB21256805): Key Value kIOMaximumSegmentCountRead/WriteKey 1 kIOMaximumSegmentByteCountRead/WriteKey 16384 kIOMinimumSegmentAlignmentByteCountKey 16384 kIOMaximumSegmentAddressableBitCountKey 64 kIOMinimumHBADataAlignmentMaskKey 0xFFFFFFFFFFFFFFFF maxTransferSize (UserGetDMASpecification) 16384 (alignment = 1, numAddressBits = 64) That satisfies maxTransferSize >= SegmentCount × SegmentByteCount (16384 = 1 × 16384), and the failing transfer is only 4 KiB. I would strongly recommend writing your own test tool that generates arbitrarily sized I/O across a broad size range Did that — byte-verified matrix over buffer alignments × sizes from 512 B to 128 MiB. Everything passes except one shape. Same device, same offset, same 4 KiB size, only the buffer placement varies: buffer within one 16 KiB page → OK (any alignment) block-aligned buffer straddling a page (e.g. page+0x3000, 8 KiB) → OK, split at the crossing sub-block-aligned buffer straddling a page (e.g. page+0x3800, 4 KiB) → EIO, deterministic From the IOStorageFamily source this looks like IOBreaker::getBreakSize() truncating the split point to a device-block multiple and getting 0 for that shape, so the request fails with kIOReturnDMAError before anything reaches my dext — but I may well be misreading it. So, two questions: Am I doing something wrong in my configuration, or misreading how these buffers are supposed to be handled? If not — what should happen to a sub-block-aligned, page-straddling buffer, and whose job is it to make it deliverable? The behavior reproduces with a similar configuration: Condensed controller source ioreg -w0 -r -c IOUserSCSIParallelInterfaceController
Topic: App & System Services SubTopic: Drivers Tags:
Jul ’26
Reply to # IOUserSCSIParallelInterfaceController: `UserGetDataBuffer` returns a zero-filled buffer for some writes
Thanks Kevin. I went through your answer point by point with experiments — here's what I found. it's not necessarily true that pwrite DID return EIO (at least for that log message) I wasn't sure either, so I traced it with dtruss — in my case it really is pwrite(2) returning EIO (fd 3 = the raw device): pwrite(0x3, ..., 0x1000, 0x2000) = 4096 0 pwrite(0x3, "x\0", 0x1000, 0x0) = -1 Err#5 ← the superblock write write_nocancel(0x2, "nx_format:308: failed to write superblock to block 0: 5 - Input/output error\n", 0x4D) lldb shows the failing buffer is a 32-byte-aligned heap pointer, so whether it straddles a page boundary is an allocator lottery — which also explains why diskutil eraseDisk only fails some of the time. the problem is that you haven't properly configured all of the keys required by UserReportHBAConstraints All seven keys are defined (and also republished as node properties via SetProperties, per FB21256805): Key Value kIOMaximumSegmentCountRead/WriteKey 1 kIOMaximumSegmentByteCountRead/WriteKey 16384 kIOMinimumSegmentAlignmentByteCountKey 16384 kIOMaximumSegmentAddressableBitCountKey 64 kIOMinimumHBADataAlignmentMaskKey 0xFFFFFFFFFFFFFFFF maxTransferSize (UserGetDMASpecification) 16384 (alignment = 1, numAddressBits = 64) That satisfies maxTransferSize >= SegmentCount × SegmentByteCount (16384 = 1 × 16384), and the failing transfer is only 4 KiB. I would strongly recommend writing your own test tool that generates arbitrarily sized I/O across a broad size range Did that — byte-verified matrix over buffer alignments × sizes from 512 B to 128 MiB. Everything passes except one shape. Same device, same offset, same 4 KiB size, only the buffer placement varies: buffer within one 16 KiB page → OK (any alignment) block-aligned buffer straddling a page (e.g. page+0x3000, 8 KiB) → OK, split at the crossing sub-block-aligned buffer straddling a page (e.g. page+0x3800, 4 KiB) → EIO, deterministic From the IOStorageFamily source this looks like IOBreaker::getBreakSize() truncating the split point to a device-block multiple and getting 0 for that shape, so the request fails with kIOReturnDMAError before anything reaches my dext — but I may well be misreading it. So, two questions: Am I doing something wrong in my configuration, or misreading how these buffers are supposed to be handled? If not — what should happen to a sub-block-aligned, page-straddling buffer, and whose job is it to make it deliverable? The behavior reproduces with a similar configuration: Condensed controller source ioreg -w0 -r -c IOUserSCSIParallelInterfaceController
Topic: App & System Services SubTopic: Drivers Tags:
Replies
Boosts
Views
Activity
Jul ’26