[DriverKit SCSI] SCSI probe stalls for Target ID > 0 with IOUserSCSIParallelInterfaceController

Hello everyone,

We are migrating a KEXT storage driver to DriverKit. In our KEXT, we use a "one LUN = one Target" model and successfully create multiple targets in a loop during initialization. We are now trying to replicate this architecture in our DEXT.

The issue is that only Target 0 is fully probed and mounted. For Target 1, the lifecycle silently stops after the first TEST UNIT READY command is successfully acknowledged. The macOS SCSI layer never sends any subsequent probe commands (like INQUIRY) to this target.

The failure sequence for Target 1, observed from our logs (regardless of whether Target 0 is created), is as follows:

  1. AsyncCreateTargetForID(1) -> UserInitializeTargetForID(1) (Succeeds)
  2. UserProcessParallelTask(Target: 1, Opcode: TUR) (Succeeds)
  3. The DEXT correctly acknowledges the TUR command for Target 1 by returning kSCSITaskStatus_CHECK_CONDITION with UNIT ATTENTION in the Sense Data (Succeeds)
  4. <-- Breakpoint -->
  5. UserProcessParallelTask(Target: 1, Opcode: INQUIRY) (Never happens)

Through log comparison, we have confirmed that the DEXT's response to the TUR command for Target 1 is identical to the successful KEXT's response.

We have tried creating only Target 1 (skipping Target 0 entirely), but the behavior is exactly the same -> the probe still stalls after the TUR.

We initially suspected a race condition caused by consecutive calls to AsyncCreateTargetForID(). We attempted several methods to ensure that targets are created sequentially, such as trying to build a "creation chain" using OSAction completion handlers. However, these attempts were unsuccessful due to various compilation errors and API misunderstandings.

In any case, this "race condition" theory was ultimately disproven by our experiment where creating only Target 1 still resulted in failure.

We would like to ask two questions:

  1. Is our inability to have a Target ID greater than 0 fully probed by macOS a bug in our own code, or could there be another reason we are unaware of?
  2. If we do indeed need a "one-after-another" creation mechanism for AsyncCreateTargetForID, what is the correct way to implement a "chained creation" using OSAction completion handlers in DriverKit?

Thank you for any help or guidance.

Best Regards,

Charles

  1. Is our inability to have a Target ID greater than 0 fully probed by macOS a bug in our own code, or could there be another reason we are unaware of?

It's a bug in your code.

  1. If we do indeed need a "one-after-another" creation mechanism for AsyncCreateTargetForID, what is the correct way to implement a "chained creation" using OSAction completion handlers in DriverKit?

You do NOT need to do this. The snippet in our documentation has a model of "one event per ID", but our production DEXT is just using a single call with a set of nested loops checking IDs and calling UserCreateTargetForID.

In terms of what could be going wrong, a few thoughts:

  • Looking through the method list, what do you return for "UserReportHBAHighestLogicalUnitNumber"? If you didn't implement it or return "0", then I think you'd get exactly the behavior you're seeing. Make sure you check this first, as this is the best idea I've come up with.

  • Look at your iig and audit it for "QUEUENAME". Most DEXTs use VERY few of these and they shouldn't be on any of "our" methods. In early development, I'd actually expect that the ONLY use would be for target creation.

  • Is Target ID 0 "fully" functional, meaning you get the entire storage stack to build out and can do I/O to it? It's fine if it isn't (after all, you're not done...), but if it isn't I would take a look in IORegistryExplorer to validate how far you're "getting" and that all the properties look right.

We have tried creating only Target 1 (skipping Target 0 entirely), but the behavior is exactly the same -> the probe still stalls after the TUR.

  • The fact you're failing on "1+", not just "the second" implies that there could be some kind of issue on the I/O side of this. As an "odd" test, what happens if you tell the system it's "1" but you ACTUALLY do everything to "0"? Or the reverse (tell the system "0", but actually target "1")?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi Kevin,

Thank you very much for your guidance. I am happy to report that the issue has been resolved. I would like to share the root cause and the final solution here in case it helps other developers facing similar issues.

As you suspected, it was indeed a bug in our code regarding capability reporting. Although we were successfully calling AsyncCreateTargetForID(1), the macOS SCSI stack was silently aborting the probe sequence after the initial TEST UNIT READY command.

The issue was located in our implementation of UserReportHighestSupportedDeviceID. We were mistakenly hardcoding the return value to 0:

// Buggy implementation
kern_return_t IMPL(DRV_MAIN_CLASS_NAME, UserReportHighestSupportedDeviceID)
{
    *id = 0; // Reported that the HBA only supports ID 0
    return kIOReturnSuccess;
}

Because the HBA reported a maximum supported ID of 0, the macOS SCSI Family treated Target 1 as an "out-of-bounds" device. While the object was instantiated and visible in ioreg, the kernel refused to Register or Match it. This explained why Target 1 remained in a !registered state and never received an INQUIRY command.

We updated the method to return the correct maximum ID supported by our controller (63 to support up to 64 targets):

// Corrected implementation
kern_return_t IMPL(DRV_MAIN_CLASS_NAME, UserReportHighestSupportedDeviceID)
{
    *id = 63; // Support for IDs 0 through 63
    return kIOReturnSuccess;
}

Immediately after this change, Target 1 moved to a registered, matched state in the I/O Registry. The SCSI stack successfully issued the INQUIRY command, and diskutil list now correctly identifies both RAID volumes.

Thank you again!

Best Regards,

Charles

As you suspected, it was indeed a bug in our code regarding capability reporting.

Never underestimate the power of an educated guess.

Thank you again!

You're very welcome.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

[DriverKit SCSI] SCSI probe stalls for Target ID &gt; 0 with IOUserSCSIParallelInterfaceController
 
 
Q