FABRIC-3.md: scope STALL -- full BOT-spec recovery designed, closing Milestone 2
Traced the completion-code handling before designing anything: every transfer completion (control or bulk) shares one gate that logs and bails on any non-success code -- STALL isn't distinguished from any other failure, and no recovery exists (no xHCI Reset Endpoint, no CLEAR_FEATURE(ENDPOINT_HALT)). A bounded-timeout safety net in xhci_bot_wait_for_idle() prevents a hang, but the endpoint stays wedged for everything after it. Per direct instruction, designed full recovery now rather than deferring to Milestone 8: new STALL_ERROR completion code, new xHCI Reset Endpoint + Set TR Dequeue Pointer command TRB types (neither exists today), CLEAR_FEATURE(ENDPOINT_HALT) reusing the existing control-transfer plumbing, escalating to a full Bulk-Only Mass Storage Reset on a second stall, bounded via a new retry counter mirroring the existing bot_tur_retries/XHCI_BOT_TUR_MAX_RETRIES precedent exactly. This closes the last open Milestone 2 item.
This commit is contained in:
+72
-2
@@ -121,7 +121,13 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
read-only today specifically because of this).
|
||||
|
||||
- [ ] Implement basic error/stall recovery (CSW failure status, endpoint stall clear) — at
|
||||
minimum enough to not wedge the controller on a single bad transfer.
|
||||
minimum enough to not wedge the controller on a single bad transfer. **SCOPED 2026-08-27
|
||||
(`FABRIC-3.md` §F.14)**: full BOT-spec recovery designed — new `STALL_ERROR` completion code,
|
||||
new xHCI Reset Endpoint + Set TR Dequeue Pointer commands, `CLEAR_FEATURE(ENDPOINT_HALT)` via
|
||||
existing control-transfer plumbing, escalating to a full Bulk-Only Mass Storage Reset on a
|
||||
second stall, bounded via a new retry counter mirroring `bot_tur_retries`'s own precedent.
|
||||
Real gap closed today: CSW status handling (PASS/FAILED/PHASE ERROR) already existed; nothing
|
||||
distinguished a STALL specifically, and no recovery of any kind existed before this pass.
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 3 — Block subsystem extensions
|
||||
|
||||
@@ -1435,7 +1441,7 @@ punch list hides that; a graph doesn't.
|
||||
graph TD
|
||||
W10["❌ WRITE(10) SCSI support<br/>(Milestone 2 — biggest single blocker)"]
|
||||
HOTPLUG["✅ Hotplug event surfacing<br/>CLOSED 2026-08-27, M2 (§F.3)"]
|
||||
STALL["❌ USB error/stall recovery (M2)"]
|
||||
STALL["❌ USB error/stall recovery (M2)<br/>SCOPED 2026-08-27 (§F.14) — full BOT-spec recovery designed"]
|
||||
|
||||
M6["✅ Milestone 6 — capsule PKI<br/>DONE 2026-08-26"]
|
||||
PH8["✅ Phase 8 — Zuse identity<br/>+ block-fence, DONE 2026-08-26"]
|
||||
@@ -1573,6 +1579,10 @@ finished). Dashed arrows = softer "gates/informs" relationships.
|
||||
noticing, because each only traced `homeblocks_sig_check()` itself, never the ordinary attach
|
||||
path running alongside it. Fixed now (relocate to devblock 1) rather than left to surface
|
||||
later as a real, confusing runtime failure.
|
||||
- **`STALL` closes out Milestone 2 entirely** (§F.14) — the last item on that punch list.
|
||||
Unlike most nodes this session, this one designs genuinely new protocol machinery rather than
|
||||
finding existing infrastructure already covers it: no completion-code distinction and no
|
||||
recovery of any kind existed before this pass, only a bounded-timeout safety net.
|
||||
|
||||
**Not yet done:** an ordered plan (which node to attack first, given the graph). Per Captain
|
||||
Bob's own framing, that's the next pass — "start asking and answering questions iteratively
|
||||
@@ -2153,6 +2163,66 @@ power is lost between the two writes) — real question, not addressed here; the
|
||||
updating from their currently-recorded devblock-0-relative assumption to devblock-1, a
|
||||
mechanical follow-up once any of this is actually coded, not re-litigated here.
|
||||
|
||||
### F.14 — `STALL` (the last M2 item) — full BOT-spec recovery designed
|
||||
|
||||
Traced the actual completion-code handling before designing anything. **Real finding: every
|
||||
transfer completion, control or bulk, shares one gate** — `if (code != XHCI_COMPLETION_CODE_SUCCESS)
|
||||
{ console_println("xhci: control transfer failed"); break; }` (`xhci.c:1267-1270`). A STALL
|
||||
isn't distinguished from any other failure today, and there is no recovery of any kind — no
|
||||
xHCI Reset Endpoint command, no USB `CLEAR_FEATURE(ENDPOINT_HALT)`. There is a real safety net
|
||||
though: `xhci_bot_wait_for_idle()` (`xhci.c:831-839`) has a bounded iteration count and returns
|
||||
`BOT_STATUS_TIMEOUT` rather than hanging forever, since the generic bail-out never resets
|
||||
`bot_cmd_kind`. So today's failure mode is "clean timeout, endpoint left permanently wedged for
|
||||
everything after it" — not a crash, but not recoverable either.
|
||||
|
||||
Also confirmed by inspection: none of the pieces real recovery needs exist yet —
|
||||
`XHCI_COMPLETION_CODE_STALL_ERROR` isn't defined (only `_SUCCESS`), nor are the `RESET_ENDPOINT`
|
||||
(xHCI spec value 14) or `SET_TR_DEQUEUE_POINTER` (value 16) command TRB types. Per direct
|
||||
instruction, full recovery is designed now rather than deferred to Milestone 8.
|
||||
|
||||
**Decisions made 2026-08-27 — the recovery sequence, mirroring the existing bounded-retry
|
||||
precedent `bot_tur_retries`/`XHCI_BOT_TUR_MAX_RETRIES` already establishes for TUR:**
|
||||
|
||||
1. **Distinguish the completion code.** Add `XHCI_COMPLETION_CODE_STALL_ERROR` (6, per xHCI
|
||||
spec) alongside the existing `_SUCCESS` (1). The shared completion-code gate branches: a
|
||||
plain non-success/non-stall code keeps today's behavior (log and bail); a stall specifically
|
||||
enters the new recovery path below instead of falling straight to the generic message.
|
||||
2. **xHCI-level reset (two new command TRB types + two new command functions, mirroring the
|
||||
existing `xhci_cmd_disable_slot()`/`xhci_cmd_configure_endpoint()` shape exactly):**
|
||||
- `xhci_cmd_reset_endpoint(dev, slot_id, ep_id)` — new `XHCI_TRB_TYPE_RESET_ENDPOINT_CMD`
|
||||
(14), transitions the stalled endpoint from Halted back to Stopped in the xHC's internal
|
||||
context. Which endpoint is "the stalled one" is already known — it's whichever of
|
||||
`bulk_in_ep_addr`/`bulk_out_ep_addr` the in-flight transfer purpose
|
||||
(`BOT_DATA_IN`/`BOT_DATA_OUT`/`CBW_SENT`/`CSW_RECEIVE`) was using, both already tracked
|
||||
fields.
|
||||
- `xhci_cmd_set_tr_dequeue_pointer(dev, slot_id, ep_id, new_dequeue, dcs)` — new
|
||||
`XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD` (16), repositions the ring's dequeue pointer
|
||||
past the failed TRB so the next enqueued transfer resumes cleanly.
|
||||
3. **USB-level clear (reuses the existing control-transfer infrastructure already built for
|
||||
`GET_DESCRIPTOR`/`SET_CONFIGURATION` — no new transfer-stage machinery, just a new request
|
||||
payload):** send `CLEAR_FEATURE(ENDPOINT_HALT)` (standard request, `wValue=0`, `wIndex=`
|
||||
the stalled endpoint address) to clear the device's own halt condition and reset its data
|
||||
toggle.
|
||||
4. **Escalation on a second stall (or if step 3 itself stalls):** issue Bulk-Only Mass Storage
|
||||
Reset (BOT class request, `bmRequestType=0x21`, `bRequest=0xFF`, no data stage — again the
|
||||
existing control-transfer machinery, a different request payload) followed by
|
||||
`CLEAR_FEATURE(ENDPOINT_HALT)` on *both* bulk endpoints (BOT spec 5.3.4's full procedure),
|
||||
then retry the original command from scratch — the same "fresh target" restart shape the
|
||||
existing TUR-chain retry already uses.
|
||||
5. **Bounded, not infinite:** a new `bot_stall_recoveries` counter capped at a new
|
||||
`XHCI_BOT_STALL_MAX_RECOVERIES`, exact value TBD but same shape as `XHCI_BOT_TUR_MAX_RETRIES`.
|
||||
Exhausting it sets `bot_last_status = BOT_STATUS_FAILED` and `bot_cmd_kind = BOT_CMD_NONE`
|
||||
explicitly — a clean terminal failure signaled to `xhci_bot_wait_for_idle()`'s caller,
|
||||
rather than relying purely on the outer timeout the way today's un-recovered stall does.
|
||||
|
||||
**Not yet scoped (deferred within this node):** the exact `XHCI_BOT_STALL_MAX_RECOVERIES` value;
|
||||
whether a `WRITE(10)`-specific residual-data concern exists on a stalled Data-Out stage (a
|
||||
partially-written SCSI command's recovery semantics may differ subtly from a stalled Data-In
|
||||
read — not analyzed here, `WRITE(10)` itself still doesn't exist); real-hardware validation is
|
||||
explicitly out of reach until Milestone 8, same caveat `WRITE(10)`'s own CSW-residue question
|
||||
carries (§F.1) — this design is believed spec-correct but has only QEMU (which rarely if ever
|
||||
issues real stalls) to test against for now.
|
||||
|
||||
### D.5 — Scope expansion (2026-08-27): identity is common to every VM, not just users
|
||||
|
||||
Surfaced while scoping `ACLKEY`, stated directly: *"the whole object is to deliver a
|
||||
|
||||
Reference in New Issue
Block a user