xhci: fix Configure Endpoint completion drop under concurrent multi-device enumeration
Root cause of the FABRIC-3.md §IX.5 follow-on: with 9 devices attached concurrently at boot (Zuse + 8 identities), only 1 of 9 ever completed enumeration and reached blkio_usb: MSC device ready -- the other 8 produced no error and no success, just silence. xhci_poll_events()'s deferred per-slot dispatch loop submitted a Configure Endpoint command (a Command Ring op) unconditionally for every slot with that action pending in a single pass -- unlike every other Command Ring op in this driver (Enable Slot, Address Device, Disable Slot), which is correctly gated behind dev->connect_state == XHCI_CONN_IDLE before ever submitting. With 2+ devices enumerating concurrently, this let multiple Configure Endpoint commands sit outstanding on the Command Ring at once. Their completion is correlated purely via the single shared dev->connect_state field (== XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT), not the completion event's own Slot ID -- so whichever slot's completion happened to land while connect_state still read AWAIT_CONFIGURE_ENDPOINT got correctly chained into SET_CONFIG, and every other slot's completion arrived after connect_state had already moved on, silently swallowed by the handler's generic "unrelated command completion" catch-all. No error path exists for this, which is why it produced total silence rather than a diagnosable failure. Root-caused live via temporary WARN-level diagnostic probes (written, captured, and fully reverted per the project's own probe convention -- this commit contains only the functional fix and its explanatory comment, no probe code) added at four points: the initial port scan, the connect handler, the Command Completion Event handler, and the deferred dispatch loop itself. The probes showed all 9 devices correctly completing Enable Slot + Address Device (ruling out the connect-state queue as the cause, the original hypothesis), then all 9 correctly submitting Configure Endpoint and all 9 commands completing successfully in hardware (code= SUCCESS, no errors logged) -- but only 1 of 9 ever got its next_action chained to SET_CONFIG. Fix: apply the same single-in-flight discipline this driver already uses for every other Command Ring op. If the Ring isn't free when a slot's Configure Endpoint action is due, put the action back on that slot instead of submitting a second command onto a busy Ring -- the next tick's dispatch pass retries it once the Ring frees up. Verified live: booting Zuse + all 8 identity drives concurrently (9 devices, one per real xHCI port via the XHCI_PORTS fix from the previous commit) now produces 9 "MSC device ready" lines and zero xHCI errors, where it previously produced exactly 1. Three-arch clean qemu acceptance (single Zuse device, the standard regression case) passed on amd64, aarch64, and riscv64 -- no change in that baseline behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
30c26ade3d
commit
e10fb76fb2
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-07T00:13:26Z -->
|
<!-- Generated by mkcapsule --manifest 2026-09-07T12:52:21Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2213,6 +2213,43 @@ void xhci_poll_events(void)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT:
|
case XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT:
|
||||||
|
/* Configure Endpoint is a Command Ring op, same as Enable
|
||||||
|
* Slot/Address Device/Disable Slot -- but unlike those
|
||||||
|
* (gated by xhci_handle_port_connected()'s own
|
||||||
|
* connect_state == XHCI_CONN_IDLE check before ever
|
||||||
|
* submitting), this deferred dispatch loop used to submit
|
||||||
|
* it unconditionally for every slot with the action
|
||||||
|
* pending in a single pass. With 2+ devices enumerating
|
||||||
|
* concurrently, that let multiple Configure Endpoint
|
||||||
|
* commands sit outstanding on the Command Ring at once --
|
||||||
|
* and their completion is correlated purely by
|
||||||
|
* dev->connect_state == XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT
|
||||||
|
* (see that branch in the Command Completion Event
|
||||||
|
* handler above), a single global flag, not the event's
|
||||||
|
* own Slot ID. Whichever slot's completion actually
|
||||||
|
* lands while connect_state still reads
|
||||||
|
* AWAIT_CONFIGURE_ENDPOINT gets correctly chained into
|
||||||
|
* SET_CONFIG; every other slot's completion arrives after
|
||||||
|
* connect_state has already moved on (to another slot's
|
||||||
|
* own Configure Endpoint, or unrelated activity) and is
|
||||||
|
* silently swallowed by the handler's generic "unrelated
|
||||||
|
* command completion" catch-all -- permanently stalling
|
||||||
|
* that slot's enumeration with no error ever logged.
|
||||||
|
* Confirmed live 2026-09-07 (FABRIC-3.md §IX.5 follow-on):
|
||||||
|
* 9 devices attached concurrently, only 1 of 9 ever
|
||||||
|
* reached SET_CONFIG/attach.
|
||||||
|
*
|
||||||
|
* Fix: apply the same single-in-flight discipline this
|
||||||
|
* driver already uses for every other Command Ring op --
|
||||||
|
* only submit if the Ring is actually free
|
||||||
|
* (connect_state == XHCI_CONN_IDLE); otherwise put this
|
||||||
|
* slot's action back so the very next tick's dispatch
|
||||||
|
* pass retries it once the Ring frees up, rather than
|
||||||
|
* racing a second command onto it. */
|
||||||
|
if (dev->connect_state != XHCI_CONN_IDLE) {
|
||||||
|
nms->next_action = XHCI_NEXT_ACTION_CONFIGURE_ENDPOINT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
dev->connect_state = XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT;
|
dev->connect_state = XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT;
|
||||||
if (xhci_cmd_configure_endpoint(dev, next_slot_id) != 0) {
|
if (xhci_cmd_configure_endpoint(dev, next_slot_id) != 0) {
|
||||||
log_message(LOG_ERROR, "xhci: deferred configure endpoint request setup failed");
|
log_message(LOG_ERROR, "xhci: deferred configure endpoint request setup failed");
|
||||||
|
|||||||
Reference in New Issue
Block a user