[test] fixes spurious mailbox/USB stalls during pre-sleep handshake - #2923
Conversation
9fbe1fc to
1b2e621
Compare
| // many ms past the ACK timestamp before unblocking the test thread. | ||
| // This gives the host's ACK response cycle time to complete on the | ||
| // wire before the test continues into System.sleep and tears down USB. | ||
| static constexpr system_tick_t ACK_DELAY_MS = 100; |
There was a problem hiding this comment.
This should not be needed I think. If we got the ACK the other side 100% got the message, right?
There was a problem hiding this comment.
This was absolutely needed... and to some extent I can't see a way around having a tiny forced delay on the device side that waits for INIT-CHECK-END vendor request protocol to complete and the host to be in a good state before the device returns from the pushMailbox call and proceeds to System.sleep calls that can pull the rug out from under the host.
- The host essentially sends the ACK INIT: OUT control transfer
- Host starts checking for CHECK: IN control transfer of the ACK response.
- Device receives the ACK, complete_'s and sends ACK response... but crucially waits up to 100ms so host has time to receive the ACK response.
- Host finalizes transaction with END: control OUT to device.
It might seem like what is the point if the ACK has it's own "ack" that we need to busy wait for... but something about the ACK INIT-CHECK-END resolves the kernel driver bug just by changing the shape of the transfers. It might be because the ACK response is empty, header only. Vs. the readMailbox and getStatus transfers that request data with a 64 byte buffer and generally have a larger payload.
1b2e621 to
d9928b7
Compare
Problem
wiring/sleep20suite intermittently failed on Linux test rigs (kernel 6.8 / AMD Rembrandt USB4 XHCI) with the device-sidepushMailboxreturning success while the host-side runner observed a USB control-transfer stall onthe corresponding mailbox read. The device would then race into
System.sleepand tear down USB before the runner could re-issue the read, de-syncing the suite and triggering a fixture-reset cascade.xhci_hcdbug: the driver synthesizes-EPIPE(LIBUSB_TRANSFER_STALL) on control IN URBs even when the wire-level data stage completed and was ACKed by host hardware. Empirically, the stalls cluster on the larger-payload IN transfers (wLength=64, which carry actual response data) and are suppressed when a vendor control transaction with smaller-payload IN reads (no response payload,wLength=14–15) is interleaved between two heavy-payload exchanges. The exact xhci_hcd state being reset isn't understood; we treat the interleaved transaction as a behavioral workaround at the protocol layer rather than a known mechanism fix.Secondary issues:
pushMailboxreported success as soon as the reply DATA stage went on the wire (HAL TX_COMPLETED), not when the host actually received it.pushMailboxdiscarded the entry's result code, so aSYSTEM_ERROR_CANCELLEDfrom the system path could not propagate to the test.delay(3s)after everypushMailboxas a workaround for the missing host-receipt signal, which helped but was not the fix.Solution
c:'A'ACK request that the runner sends after parsing each mailbox reply. This is what completes the entry on the device side AND interleaves an additional vendor transaction (whose IN reads carry no payload, so they're the kernel-bug-safe variety) between the readMailbox and getStatus exchanges, suppressing the phantom-EPIPE stalls observed without it.MailboxEntrywith a monotonicid; include it in the read reply and in the ACK request so completion is idempotent and can't match the wrong entry.readMailbox's TX_COMPLETED-driven completion callback. Completion now happens from the ack handler instead.MailboxEntry::waitloop forACK_DELAY_MS(100 ms) past the ACK timestamp before returning, so the host's ACK response cycle finishes on the wire before the test continues intoSystem.sleepand detaches USB.pushMailboxreturnm->result()so a cancellation propagates to the test instead of being reported as success.pushMailbox(..., 10000)site insleep20_device.cppto20000ms for headroom under retry.delay(3s)workaround that followed everypushMailboxcall insleep20_device.cpp; the ACK mechanism replaces what the delay was guarding against.Testing
wiring/sleep20on the affected rig: 46/46 tests passing, zero application-level USB stalls observed in usbmon captures.References
fix/test-runner-mailbox-usb-stall(indevice-os)fix/mailbox-usb-stall