diff options
| author | Baptiste Daroussin <bapt@FreeBSD.org> | 2026-02-15 18:07:07 +0000 |
|---|---|---|
| committer | Baptiste Daroussin <bapt@FreeBSD.org> | 2026-02-16 08:14:05 +0000 |
| commit | 38c18332642500fdfe075a82f88e033f6673a53f (patch) | |
| tree | 3cce19333acf5798c9e3913bddefd32dc1e21922 | |
| parent | be522176951d8b542de9354f4ec9ac7603745b71 (diff) | |
libusb: dequeue next transfer on completion to prevent stalls
The transfer proxy callbacks (bulk/interrupt, control, isochronous)
only called libusb10_submit_transfer_sub() in the START path to
pipeline the second kernel transfer slot. On completion or error,
no attempt was made to dequeue the next pending transfer from
tr_head onto the now-free slot.
When more than two async transfers were submitted on the same
endpoint, the third (and subsequent) transfers would remain stuck
on tr_head indefinitely, since no completion ever triggered their
submission. This caused a protocol-level deadlock in applications
like adb that submit header + payload + zero-length terminator as
three separate bulk transfers in sequence.
Fix by calling libusb10_submit_transfer_sub() after every
libusb10_complete_transfer() in all three proxy callbacks.
MFC After: 2 weeks
Reviewed by: adrian
Differential Revision: https://reviews.freebsd.org/D55289
| -rw-r--r-- | lib/libusb/libusb10.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index e226def0b7b6..b3af017ab980 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -1259,6 +1259,9 @@ libusb10_isoc_proxy(struct libusb20_transfer *pxfer) libusb20_tr_get_length(pxfer, i); } libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; case LIBUSB20_TRANSFER_START: /* setup length(s) */ @@ -1281,6 +1284,9 @@ libusb10_isoc_proxy(struct libusb20_transfer *pxfer) break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } } @@ -1322,11 +1328,15 @@ libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer) } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } /* FALLTHROUGH */ @@ -1353,6 +1363,8 @@ libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer) default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } } @@ -1397,11 +1409,15 @@ libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } /* FALLTHROUGH */ @@ -1442,6 +1458,8 @@ libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + /* start next queued transfer, if any */ + libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; } } |
