Repository navigation
darwin: allow cancelling bulk stream transfers - #1
Merged
Merged
Conversation
darwin_cancel_transfer() routes bulk, interrupt and isochronous transfers to darwin_abort_transfers(), which already knows how to abort a single bulk stream with AbortStreamsPipe(). LIBUSB_TRANSFER_TYPE_BULK_STREAM was missing from the switch, so libusb_cancel_transfer() returned LIBUSB_ERROR_INVALID_PARAM for every stream transfer. USB Attached SCSI uses the command tag as the bulk stream id, so a redirection host cancels stream transfers constantly. When the cancel is not applied the transfer stays in flight on the host controller and the guest's command never completes, which surfaces as a 30 second SCSI timeout and a device reset in the guest. A caller that cancels its transfers and waits for them to complete, as usbredirhost does when it releases a device, waits forever while a stream transfer is pending, and USB Attached SCSI keeps one pending on its status pipe. Assisted-by: OpenCode:deepseek-v4.1-flash Assisted-by: Claude:claude-fable-5-2
UTMapp-bot
pushed a commit
to UTMapp-bot/UTM
that referenced
this pull request
Nov 11, 2026
Disconnecting a USB drive that the guest uses through USB Attached SCSI (most USB 3 drives and SSD enclosures) froze UTM: the USB menu and every other part of the app stopped answering, and after UTM was ended the drive did not come back to macOS until it was replugged. When a device is released, usbredirhost cancels its transfers and waits for them to complete. libusb on macOS refused to cancel bulk stream transfers (LIBUSB_ERROR_INVALID_PARAM), and USB Attached SCSI always has one pending on its status pipe, so the wait never ended while it held the lock the main thread needs. Pick up c1c01e6593 from the UTM libusb fork (utmapp/libusb#1), which cancels bulk stream transfers the way plain bulk transfers are. The change is by giovariot, who included it in utmapp#7928. Assisted-by: Claude:claude-fable-5-2
UTMapp-bot
pushed a commit
to giovariot/UTM
that referenced
this pull request
Nov 11, 2026
Once a USB 3.2 Gen 2 device enumerates at SuperSpeed, the guest drives it as
a UAS (USB Attached SCSI) device, where the command tag is the USB 3 bulk
stream id, so usbredirhost cancels bulk stream transfers constantly.
libusb's Darwin backend did not handle LIBUSB_TRANSFER_TYPE_BULK_STREAM in
darwin_cancel_transfer(), so every cancel returned
LIBUSB_ERROR_INVALID_PARAM ("unknown endpoint type 4"). The cancelled
transfers stayed in flight on the host controller: the first UAS commands
timed out (30 second SCSI timeout, uas_eh_abort_handler, repeated device
resets), and the device was left captured by macOS after the virtual machine
stopped, until it was physically replugged.
darwin_abort_transfers() already aborts a single bulk stream with
AbortStreamsPipe(); route BULK_STREAM to it. The same omission is still
present in upstream libusb master.
The patch is c1c01e6593 from the UTM libusb fork (utmapp/libusb#1).
Resolves utmapp#7530
Assisted-by: OpenCode:deepseek-v4.1-flash
Assisted-by: Claude:claude-fable-5-2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
darwin_cancel_transfer()sends bulk, interrupt and isochronous transfers todarwin_abort_transfers(), which aborts a stream withAbortStreamsPipe()when the transfer has a stream id.LIBUSB_TRANSFER_TYPE_BULK_STREAMwas missing from the switch, solibusb_cancel_transfer()returnedLIBUSB_ERROR_INVALID_PARAMfor every stream transfer and the transfer stayed in flight.A caller that cancels its transfers and waits for them to complete then waits forever. usbredirhost does that when it releases a device (
usbredirhost_clear_device()), and USB Attached SCSI keeps a stream transfer pending on its status pipe, so disconnecting a USB 3 drive from a guest froze the application, and the drive did not return to macOS until it was replugged.The change is by giovariot, who is the commit's author; I added the last paragraph of the message. Upstream libusb's
masterhas the same gap.Testing
Tested by an AI agent on a MacBook Pro M2 Max, macOS 27.2, with libusb 1.0.25 plus this repository's patches, built with and without the change and used through spice-gtk and usbredirhost by a SPICE client. Device: a Seagate Backup Plus 500 GB drive (0bc2:ab24) on a 5 Gb/s link, redirected to an Ubuntu 26.04 arm64 guest (Linux 7.0), where it is
Driver=uas, 5000M. Only reads were done on the drive.sampleshows a worker thread inspice_usb_backend_channel_detach→usbredirhost_set_device→usbredirhost_clear_device, and the main thread waiting for the lock it holds. After the application was ended, macOS did not get the drive back until it was replugged.ddwas reading from the drive; the disk is gone from the guest each time, connecting brings it back, and reads run at 102 to 105 MB/s. Powering the guest off with the drive attached returns it to macOS without a replug.Not tested: this branch's tip itself was not built (the change was built on the 1.0.25 release with the same patches, where the function is identical), iOS, and other devices.