macoslibusbiokitkernel-extensiondriverkit

Performance issue after migrating from codeless KEXT to DEXT


I am working on migrating a codeless KEXT to DriverKit. It is used to disable the IOKit HID driver for USB devices that present themselves as HID compliant in firmware upgrade mode.

So far I have managed to match an empty subclass of IOService to the relevant devices. Here's an example of the IOKitPersonalities entries I'm using:

<dict>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleIdentifierKernel</key>
    <string>com.apple.kpi.iokit</string>
    <key>IOClass</key>
    <string>IOUserService</string>
    <key>IOProviderClass</key>
    <string>IOUSBHostInterface</string>
    <key>IOResourceMatch</key>
    <string>IOKit</string>
    <key>IOUserClass</key>
    <string>DriverKitTestExtension</string>
    <key>IOUserServerName</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>bConfigurationValue</key>
    <integer>1</integer>
    <key>bInterfaceNumber</key>
    <integer>0</integer>
    <key>idVendor</key>
    <integer><!-- USB Vendor ID --></integer>
    <key>idProduct</key>
    <integer><!-- USB Product ID --></integer>
</dict>

Is it correct to use IOUSBHostInterface as the provider class for this use case? I've also tried using the old (deprecated) IOUSBInterface, but it has the same problems as IOUSBHostInterface.

The new DEXT is working but I am seeing some bad performance regressions in our firmware update code. Calls to libusb are taking tens of seconds. If I use the old KEXT, they return immediately.

Here are some example traces of the problem:

   5 redacted 3168.0  libusb_claim_interface ..../libusb/Sources/libusb/core.c:1310
   4 redacted 3168.0  darwin_claim_interface ..../libusb/Sources/libusb/darwin_usb.c:1089
   3 IOUSBLib 1668.0  IOUSBInterfaceClass::USBInterfaceOpen(bool)
   2 IOKit 1668.0  io_service_wait_quiet
   1 libsystem_kernel.dylib 1668.0  mach_msg
   0 libsystem_kernel.dylib 1668.0  mach_msg_trap

And:

   7 redacted 1859.0  libusb_get_device_list ..../libusb/Sources/libusb/core.c:632
   6 redacted 1859.0  darwin_get_device_list ..../libusb/Sources/libusb/darwin_usb.c:780
   5 redacted 1859.0  process_new_device ..../libusb/Sources/libusb/darwin_usb.c:726
   4 redacted 1766.0  darwin_check_configuration ..../libusb/Sources/libusb/darwin_usb.c:540
   3 IOKit 1766.0  IOIteratorNext
   2 IOKit 1766.0  io_iterator_next
   1 libsystem_kernel.dylib 1766.0  mach_msg
   0 libsystem_kernel.dylib 1766.0  mach_msg_trap
   0 libsystem_kernel.dylib 1766.0  mach_msg_trap

These are recorded in the "Time Profiler" instrument with "Record Waiting Threads" enabled.

Can I do anything in the DEXT to fix this problem? I've tried subclassing IOUSBHostInterface instead of IOService, but that made no difference.

Here's the relevant output from running "ioreg -lirc IOUSBHostInterface":

+-o IOUSBHostInterface@0  
  | {
  |   "USBSpeed" = 1
  |   "iInterface" = 0
  |   "IOServiceLegacyMatchingRegistryID" = 4294971983
  |   "bInterfaceProtocol" = 0
  |   "bAlternateSetting" = 0
  |   "idProduct" = 
  |   "bcdDevice" = 292
  |   "USB Product Name" = 
  |   "locationID" = 338690048
  |   "bInterfaceClass" = 3
  |   "bInterfaceSubClass" = 0
  |   "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
  |   "USBPortType" = 0
  |   "bConfigurationValue" = 1
  |   "bInterfaceNumber" = 0
  |   "USB Vendor Name" = 
  |   "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
  |   "idVendor" = 
  |   "bNumEndpoints" = 2
  |   "IOGeneralInterest" = "IOCommand is not serializable"
  |   "IOClassNameOverride" = "IOUSBInterface"
  | }
  | 
  +-o DriverKitTestExtension  
      {
        "IOClass" = "IOUserService"
        "CFBundleIdentifier" = 
        "IOProviderClass" = "IOUSBHostInterface"
        "IOUserServerCDHash" = "faa70138281d36b53946591685ccdceba4a5d638"
        "idProduct" = 
        "IOResourceMatch" = "IOKit"
        "bConfigurationValue" = 1
        "IOProbeScore" = 90000
        "IOMatchCategory" = "com.apple.null.driver"
        "IOUserServerName" = 
        "IOMatchedPersonality" = {"IOClass"="IOUserService","CFBundleIdentifier"=" ","IOProviderClass"="IOUSBHostInterface","IOUserServerCDHash"="faa70138281d36b53946591685ccdceba4a5d638","idProduct"=,"IOResourceMatch"="IOKit","bConfigurationValue"=1,"IOMatchCategory"="com.apple.null.driver","IOUserServerName"=,"idVendor"=,"CFBundleIdentifierKernel"="com.apple.kpi.iokit","bInterfaceNumber"=0,"IOUserClass"="DriverKitTestExtension"}
        "idVendor" = 
        "CFBundleIdentifierKernel" = "com.apple.kpi.iokit"
        "bInterfaceNumber" = 0
        "IOUserClass" = "DriverKitTestExtension"
      }

Any input is welcome!


Solution

  • I finally found the problem! After reading How to set `com.apple.developer.driverkit.transport.usb` entitlement?, I decided to experiment with the entitlements for my DEXT. This made me realize that the DEXT was not being loaded at all.

    I changed the DEXT entitlements from:

    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.developer.driverkit</key>
        <true/>
        <key>com.apple.developer.driverkit.transport.usb</key>
        <true/>
    </dict>
    </plist>
    

    To:

    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.developer.driverkit</key>
        <true/>
        <key>com.apple.developer.driverkit.transport.usb</key>
        <array>
            <dict>
                <key>idVendor</key>
                <integer><!-- USB Vendor ID --></integer>
            </dict>
        </array>
    </dict>
    </plist>
    

    After this, the log message from my IOService subclass is now showing up (using log stream with grep). Performance during the firmware upgrade is back to normal.