A broken Bulk-Only transport is recovered by the class's own sequence, and one that stays broken is taken offline - #466
Conversation
…e TRBs it queued The stop this kernel performs before every reset waited for `IN_FLIGHT`, a count raised between a bulk TRB's enqueue and its completion. A Bulk-Only Transport command is three transfers (BOT 1.0 §5.1: the 31-byte CBW out, an optional data phase, the 13-byte CSW in), and between any two of them that count is zero while the device is still holding the command. Every completed boot therefore wrote `usb-quiesce: no bulk transfer was outstanding, so this reset cuts none` — true of the TRBs and not of the protocol — and a boot ended by the deadline in one of those gaps port-reset a device waiting for data it will never get. §6.7.2/§6.7.3 leave such a device waiting; the class's way out is §5.3.4's Reset Recovery, three control transfers a wedged kernel cannot issue. `toyos-xhci`'s new `bot` module is the decision: six phases the driver publishes as it walks a round trip, and what a reset owes a device in each. A transfer already queued is the controller's to finish, so re-queueing one is refused by the decision rather than by the site — for an out data phase, that would be a second write of the block. `xhci::stop` publishes the one open command the way it already publishes its controllers, because the CPU holding `XHCI` may be the wedged one the reset is ending: the device's pool block, its doorbell window, the data phase the CBW promised, and where each bulk ring's next enqueue point is. `settle_commands` then gives the driver its own bound to close the command, and where that does not happen it queues the data the device is owed and a read of the CSW, rings the doorbells and polls the CSW's own signature out of DMA. Nothing reads the event ring, which is the one structure the wedged CPU is still the consumer of. `InFlight` is gone: the command's guard spans the whole round trip and subsumes it. On a boot with nothing open the path costs one relaxed load, as before. `usb-wedge-mid-write` is the stimulus: at the shutdown syscall, after the job list, it rewrites block 0 of the boot stick with the bytes it has just read from it and stops every CPU between that command's CBW and its data phase, leaving only the boot deadline to end the machine. The write is byte-for-byte idempotent, so the medium is what it was however much of it the reset completes — which is what lets a control that deliberately cuts a write run against the bench's own stick. `cargo test --lib`: 297 passed, exit 0. `cargo test --workspace --exclude toyos-build`: exit 0, 138 suites ok. `cargo test` in toyos-xhci: 49 passed, exit 0. `cargo clippy --workspace --exclude toyos-build --all-targets -- -D warnings`: exit 0. `cargo clippy -p toyos-build --all-targets -- -D warnings`: exit 0. The kernel is UNBUILT on this commit: `cargo run -- --build-only` refuses because the machine-wide sysroot is held by /Users/jan/Dev/jan/toyos-aperture for an unlanded `toyos-abi/src/boot.rs` change. No T14 boot has run, so `boot.usbwedge.*` carries ceilings and no readings, and `usb_reset_finishes_an_open_command` carries the `UNMEASURED` marker that buys its one CI measurement.
…t a second spelling of it
`usb_reset_hands_devices_back` held its own copy of the needle — "bulk transfer"
— beside the one in `metaldevices::LOADER_RECORDS`. Renaming the kernel's line
moved one and left the other, so all four arms failed by name against a kernel
that settles strictly more than it used to:
FAIL usb_reset_hands_devices_back: 4 of 4 reset path(s) unmet:
the orderly reboot: the account says nothing about a bulk transfer, ...
A second spelling of a predicate is one that goes on passing after the kernel
stops writing the line, which is the failure this one only just avoided by
failing loudly instead. The gate now reads `metaldevices::QUIESCE_COMMAND`.
`cargo test --test toyos-build usb_reset_hands_devices_back -- --nightly`: exit 0,
1 passed, all four paths reporting `and the stick enumerated again`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ts image `metalprofile` refuses a boot whose list nobody has priced: [metal] the boot "usbwedge" runs 0 job(s) and list.usbwedge.job_ms prices none of them; a list nobody has priced cannot be sized to the runner's bound It is `deadlinewedge`'s list — one job and a wedge — taken one write later, so it carries that row's ceiling and says so. With it, `cargo test --test toyos-build usb_reset_finishes_an_open_command -- --metal --metal-readback target/metal-usbhang` stages target/metal-usbhang/usbwedge/image.img, armed with root=8ca4ff257e3ea079676f438e6298489f,usb-wedge-mid-write,boot-deadline=120000 read out of the image itself, and exits 2 (Verdict::Staged — the machine was not touched). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…taged on it Reverted here and reverted back in the very next commit. This tree exists to build one image and nothing else, and it is on the branch so that the image the T14 refuses came from a committed tree rather than from a working copy nobody can reproduce. Everything the fix touched goes back to `origin/main`: `xhci::stop` settles `IN_FLIGHT` again, `msc` publishes nothing, `normal_trb` and `Dma::from_addr` are gone, and `toyos_xhci::bot` does not exist. What stays is the stimulus — `usb-wedge-mid-write`, its `mid_write` module, and the one call between the CBW phase and the data phase — so this boot wedges in exactly the state the green arm wedges in and the machine answers about the reset alone. `cargo test --lib` reds on this commit by construction: `the_kernel_writes_the_quiesce_lines_the_host_reads` holds the host's needles to a `stop.rs` that no longer writes them. That is the revert being complete, not a second defect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… wedge staged on it" This reverts commit 3fb10e2. The control image is built and kept (target/metal-usbhang-control/usbwedge/image.img, sha256 9e917340770a4d7f5a616b0e5a956fa387214deed32c0fb17006151bf693b00a), and the tree it came from is in this branch's history where anybody can rebuild it. What it measured on the way past, with the reverted kernel in the guest: FAIL usb_reset_finishes_an_open_command: "the device answered with a CSW of status" never reached the boot after the reset test result: FAILED. 0 passed, 1 failed, 1 total (226.7s) — red twice, the same failure both times, against the same base the green arm passed on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… three — and the account now fits on the page
Run 39 flashed the control — the base's reset path with the wedge on it — and
the stick came back: `back_secs 225`, `stick_secs 0`, EXIT=0. The stimulus fired
and the deadline ended the machine at 120062 ms, so a SanDisk Ultra holding a
CBW with nothing queued for its data phase survives this platform's port reset.
The green arm was not run: a control that does not kill the stick measures
nothing about the fix.
The recorded kills (runs 25/26 ccorpus, run 33 metalcase) were sustained
multi-megabyte writes. So the stimulus now varies both things that could matter.
Every arm moves 4 MiB to the device first — 64-block pairs read and written back
byte for byte at the disk's own end, because the bench's stick is thirty
gigabytes and the image on it is eighty megabytes — and takes the wedge inside
the very next command, at one of three phases:
usb-wedge-data-owed the CBW is taken and no data is queued
usb-wedge-in-data a data TRB is on the ring and its doorbell unrung
usb-wedge-before-status the data is taken and nothing reads the CSW
A boot carries exactly one; `quiesce` refuses two, because which phase a device
is stopped in is the whole measurement.
`usb-wedge-in-data` is ground the first design could not reach, and it found a
hole: the driver publishes its phase *between* the enqueue and the doorbell, so
a machine stopped there leaves a TRB the controller was never told about, and
`bot::owed` treated "queued" as "the controller's to finish". The reset would
have waited out a data phase nothing was moving. `Owed::ring_data` is the fix.
And the page: run 39's `loader.log` carries `older records dropped to fit this
page: 138` and **no `usb-quiesce` lines at all**. `Report::tail` fills to
`TEXT_BYTES`, so `blackbox::append`'s `Report::reopened` wrote into zero bytes
and said nothing about having done so. The reset's account — the only evidence
there is about the reset — was silently absent on the boot it exists for.
`readbacks/deadlinewedge` and `readbacks/hardlockup` carry none either, so both
existing wedge arms have been losing it since they were written.
`toyos_blackbox::ACCOUNT_BYTES` now reserves an eighth of the page for it.
cargo test in toyos-blackbox: 24 passed, exit 0
cargo test in toyos-xhci: 50 passed, exit 0
cargo test --lib: 297 passed, exit 0
cargo test --test toyos-build usb_reset_finishes_an_open_command: exit 0,
PASS (61s), all three phases:
usb-wedge-data-owed: stopped in its data (unqueued) phase, and the reset read the CSW
usb-wedge-in-data: stopped in its data phase, and the reset read the CSW
usb-wedge-before-status: stopped in its status (unqueued) phase, and the reset read the CSW
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…phases staged on it Reverted here and reverted back in the very next commit. This tree exists to build three images and nothing else, and it is on the branch so that the images the T14 refuses came from a committed tree rather than from a working copy nobody can reproduce. `xhci::stop` settles `IN_FLIGHT` again, `msc` publishes no phase, and `normal_trb` and `Dma::from_addr` are gone: `git diff origin/main HEAD -- kernel/src/drivers/xhci/stop.rs kernel/src/mm/dma.rs` is empty. Two things are deliberately NOT reverted, because neither is the behaviour under test and both are what makes the control readable: - `toyos-xhci/src/bot.rs` — a pure module with no caller left in this kernel. The actuator names its phase with `bot::Phase`, and a control that could not say which phase it staged would answer the question with nothing. - `toyos-blackbox`'s `ACCOUNT_BYTES` reserve. Run 39's control lost the whole `usb-quiesce` account to a full page; without the reserve this one would too, and what the base's reset path did is exactly what these boots are for. One difference from the green arm's kernel, and the device cannot see it: on this tree `bulk` takes no phase, so the `Phase::Data` wedge is taken just before the data transfer is queued rather than between its enqueue and its doorbell. A TRB nothing has rung for is a TRB the device never sees, so both leave it in the same state — holding the CBW with no data arriving. `cargo test --lib` reds on this commit by construction: `the_kernel_writes_the_quiesce_lines_the_host_reads` holds the host's needles to a `stop.rs` that no longer writes them. That is the revert being complete. `cargo run -- --build-only`: exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…e wedge phases staged on it" This reverts commit 09a7064. The three control images are built and kept under target/metal-usbhang-control/, and the tree they came from is in this branch's history where anybody can rebuild it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…arm never lets the bus go idle
Run 43 flashed all three phase controls — the base's reset path, one per phase,
4 MiB of writes behind each — and the stick came back from every one:
`stick_secs 0`, PASS, EXIT=0. Each page shows the stimulus firing at ~2.8 s, the
deadline at ~120065 ms, and (now that the page reserves the account's bytes) the
base's own `no bulk transfer was outstanding, so this reset cuts none` followed
by the port resets. By the rule in the request no green arm ran.
What that says is about the *stimulus*, not about the device. A wedge stops CPUs
and then waits two minutes for the deadline — and the controller is not a CPU. A
TRB with its doorbell rung completes in microseconds; one never rung never
starts; a CSW nobody reads sits in the device's buffer. Two minutes later there
is nothing in flight on any of the three phases, and this platform's SanDisk
survives a port reset in every one of those idle states.
The three kills (runs 25, 26, 33) were not idle. `usb-reset-under-load` stages
what they were: from the shutdown syscall it sweeps the last eighth of the stick
— reading each 256 KiB run and writing it back byte for byte — and never stops,
so the deadline resets the machine with the controller moving bytes and the
device programming flash. A sweep and not a rewrite, so no block is programmed
twice in a boot; refused by name on a disk with no room for one, which is every
guest here and is what the QEMU half of the arm establishes.
And the account now says what the reset found rather than only what the driver
had queued: `OPEN` carries the device's own context block, and `settle_commands`
reports the data endpoint's state and how many TRBs the controller had not
reached on the ring. That is reported and never judged — which states this
platform survives is the open question, and a predicate over it would be the
suite deciding the answer.
cargo run -- --build-only: exit 0
cargo test --lib: 297 passed, exit 0
cargo test --test toyos-build usb_reset_finishes_an_open_command: exit 0, PASS (66s)
usb-wedge-data-owed: stopped in its data (unqueued) phase, and the reset read the CSW
usb-wedge-in-data: stopped in its data phase, and the reset read the CSW
usb-wedge-before-status: stopped in its status (unqueued) phase, and the reset read the CSW
usb-reset-under-load: refused by name on a disk with no room, and the boot ended
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…aged on it Reverted here and reverted back in the very next commit. This tree exists to build four images and nothing else, and it is on the branch so that the images the T14 refuses came from a committed tree rather than from a working copy nobody can reproduce. `xhci::stop` settles `IN_FLIGHT` again, `msc` publishes no phase and no context, and `normal_trb` and `Dma::from_addr` are gone: `git diff origin/main HEAD -- kernel/src/drivers/xhci/stop.rs kernel/src/mm/dma.rs` is empty. Not reverted, because neither is the behaviour under test and both are what make the control readable: `toyos-xhci/src/bot.rs` (the actuator names its phase with `bot::Phase`) and `toyos-blackbox`'s `ACCOUNT_BYTES` reserve (without it the control loses its own account to a full page, and what the base's reset did is what these boots are for). The load arm needs neither. One difference the device cannot see: on this tree `bulk` takes no phase, so the `Phase::Data` wedge is taken just before the data transfer is queued rather than between its enqueue and its doorbell. A TRB nothing has rung for is a TRB the device never sees. `cargo test --lib` reds on this commit by construction: `the_kernel_writes_the_quiesce_lines_the_host_reads` holds the host's needles to a `stop.rs` that no longer writes them. That is the revert being complete. `cargo run -- --build-only`: exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… arms staged on it" This reverts commit bf7d015. The four control images are built and kept under target/metal-usbhang-control/, and the tree they came from is in this branch's history where anybody can rebuild it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…acker keeps the finding Run 45 put the base's reset path through a write that was still in flight — the sweep never stops, so the deadline reset the machine with the controller moving bytes. The kernel's own account on that page reads usb-quiesce: 1 bulk transfer(s) were outstanding and 1 still is after 2000 ms, so this reset cuts them ... a device cut in its data phase may need a physical replug before its next host can enumerate it followed by every port reset and power-off — and the stick enumerated on the next host anyway. With runs 39 and 43 that is four states of a cut Bulk-Only command, four clean controls, and the reading this branch started from does not hold: the phase the reset finds the command in is not what loses this stick. The three kills share the hang, not the reset, and what a hang leaves the controller and the device in is not reachable by a deliberate wedge — nor knowable through the stick, which is the channel and is what dies. That finding is now `issues/kernel/four-phases-of-a-cut-command-and-the-stick-survived-all-four.md`, with the five rows and run 45's own words in it. What the bench keeps is one boot, not four. `usbload` is the owner's ruling as a standing check — a machine writing to the stick continuously, reset out from under itself, and the device enumerable afterwards — and `boot.usbload.stick_secs` is the verdict. The three phase arms measured the same idle bus under three names and are answered; they stay as QEMU arms, where they cost seconds and are what a mutation of the reset path is seen in, and their metal rows are gone. `usb_load_chain` asserts what the boot staged rather than what the device did: the sweep ran, nothing but the bound ended the machine, and the reset's account reached the page — which is `toyos_blackbox::ACCOUNT_BYTES` under test on the machine. What state the reset found the controller in is printed and not judged; this bench has four clean answers to that and no theory, and a predicate over it would be the suite deciding the question. cargo run -- --build-only: exit 0 cargo test --lib: 297 passed, exit 0 cargo test --test toyos-build usb_reset_finishes_an_open_command: exit 0, PASS (63s) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…finish its command The review sent the branch back with the machine's own pages against three of its claims. Each is answered here. THE FINISH IS DELETED. `settle_commands` rebuilt the driver's rings from numbers `OpenCommand` published and enqueued TRBs onto them. Nothing on that path can hold the controller lock — that is the whole reason `POINTS` and `OPEN` exist — so on every path but the panic one the driver's own CPU may still be enqueuing there, and for an out data phase that is a second write of the block. Five T14 controls said the finish buys nothing on this bench: a SanDisk Ultra cut while holding a CBW, while a data TRB sat unrung, while a CSW went unread, and with a write genuinely in flight all came back `stick_secs 0`. So `finish`, `bot::owed`, `bot::Owed`, `normal_trb` and the `Open`/`Device` fields only they read are gone, and what the path keeps is the publication and the account: which phase the device was left in, and what the controller says about its data endpoint. THE ACCOUNT WAS NOT LOST TO THE RESERVE. Run 43's `usb-wedge-data-owed` and `usb-wedge-in-data` pages carry zero `usb-quiesce` lines with `ACCOUNT_BYTES` in place. Measured off the pages, their reports ran to 13773 and 13763 bytes of the 14296 a report may spend, leaving 2571 and 2581 free; `usb-wedge-before-status` and run 45 spent 14253 and 14258 and carried 798 and 930 bytes of account. The page was never full. The loader recovered a valid WEDGED page from that DRAM afterwards, so `append`'s own `recover` succeeded and the closure ran — and `stop_all` writes its barrier line and then waits seconds before `Report::seal`, the one statement that rewrites the length and checksum covering everything written. A machine that ends inside one of those waits leaves every byte invisible and `append` returns true. `Report::commit` and a per-line seal-and-flush in `blackbox::append` fix it; a page now says how far the reset got. The reserve stays as the other half, and its test no longer asserts over the constant it tests — `ACCOUNT_BYTES = 0` now reds it. THE SWEEP WAS A HARD LOCKUP AND IT WRAPPED. Run 45's page reads `a cpu locked up with interrupts off: cpu7 has taken no interrupt for 60003 ms`, not the boot deadline the judge demands: the sweep never returned to a scheduler pass, so its CPU's one-shot was never re-armed. It now takes `deadline::this_cpu`'s three statements, so the deadline reaches it, and `usb_load_chain` refuses a page naming the other bound. It also wrapped, so "no block is programmed twice in a boot" — the `Flash::Ok` rationale for the owner's own stick — was false; the sweep is bounded to one pass and says so by name if it reaches the end. Also: `restart_bulk` on a stalled data phase republishes the ring it rebuilt; two armed wedge phases are refused by name instead of asserting inside the shutdown syscall; `metaldevices`' text scan reads the kernel's code and not its comments, with a negative control on the stripper; the three wedge arms leave `FLASHABLE` and `WEDGE_ARMS`, since no profile builds an image carrying one; the harness spells each arm's parameters once, with no catch-all; and every prose finding is answered by deletion, in source and in the tracker. `usb_reset_finishes_an_open_command` stays `Tier::Fast` with the one-run marker: `src/tiers.rs` refuses a Nightly row with no CI evidence and refuses the marker on a Nightly row alike, so the name costs the two CI cycles `tests/CLAUDE.md` states and this is not landable until the second. Gates: `cargo test --lib` exit 0, 298 passed; `-p toyos-blackbox -p toyos-xhci` exit 0, 25 and 46; the mutation exit 101; `--workspace --exclude toyos-build` exit 0; `cargo run -- --clippy` exit 0; `--build-only` exit 0; `usb_reset_finishes_an_open_command` PASS 64 s; `usb_reset_hands_devices_back` PASS 42 s. One T14 boot is owed and staged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… stick came back The first boot of the corrected stimulus, on 4d97210, and the first time this arm's judge has executed. Transcript in the tracker's terms: the machine answered ssh again after 219 s the boot stick enumerated 0 s after the machine answered the boot deadline expired: a bound of 120000 ms, reached at 120066 ms and no `a cpu locked up with interrupts off` line, which is what the re-armed one-shot bought: run 45 was ended half a bound early by `toyos_tco::hard_lockup_bound_ms` because that sweep took no interrupt at all. The page carried the whole account, which no wedge page this bench has taken before did: usb-quiesce: a Bulk-Only command was open in its data phase on slot 5 after 2000 ms, so this reset cuts it usb-quiesce: the controller had that device's data endpoint Running with 236 TRB(s) it had not reached on the ring Two hundred and thirty-six transfers ahead of the dequeue pointer on a Running endpoint is the hardware's own word that the reset landed mid-stream, and the device enumerated on the next host 0 s after the machine answered. So the finding grows a row and loses a claim it never had: neither the phase the reset finds the command in nor how busy the bus is when it lands is what loses this stick. The profile prices the arm from that boot — complete_ms 1204, back_secs 219, stick_secs 0 — and gains `boot.usbload.deadline_lateness_ms`, 66 ms against the ceiling `boot.deadlinewedge.deadline_lateness_ms` derives. The first judge run refused the arm for pricing no such number; a measurement with no ceiling cannot fail. `list.usbload.job_ms` stays although the boot runs no jobs, because the profile refuses a boot whose list is unpriced and says so by name. Gates: the offline judge `usb_reset_finishes_an_open_command --metal --metal-readback target/metal-usbhang` exit 0, PASS, 1 boot; `cargo test --lib` exit 0, 298 passed; `cargo run -- --clippy` exit 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… sealed a line at a time The second review sent the branch back on eight code findings. Each is answered here; every prose finding is answered by deletion. THE ONE NUMBER THE CONCLUSION RESTS ON IS NOW A TESTED REFUSAL. "236 TRB(s)" was produced in `stop.rs` by `dequeue.wrapping_sub(base) / 16` and a `% RING_SIZE`, which refused nothing: a dequeue pointer outside the ring — a context the controller never wrote, an `OPEN` read while another CPU was publishing it — wrapped into some number between 0 and 255 that the account then printed as the hardware's own word. It is now `toyos_xhci::Ring::pending`, beside `EndpointState::decode`, which reads the other half of the same endpoint context: pure, `forbid(unsafe_code)`, and `Result<u16, NotOnTheRing>`. A pointer before the ring's first TRB or past its last, one on the ring and not at a TRB, and a driver enqueue point past the ring's end are three refusals with three `Display` clauses, and the account prints the refusal where it used to print a count. Seven tests cover the wrap in both directions, both ends of the ring, the Dequeue Cycle State and the three reserved bits that ride in the same field (xHCI 1.2 §6.2.3), a base that is not a TRB boundary, and a ring of no TRBs — which is the division by zero this may not take, since it runs where the kernel may not panic. The account needed a base and a tail and never a ring, so `ring()`, `point()` and its cycle packing, `OPEN`'s host-side block address, the `unsafe Dma::from_addr` over it and the `MSC_STRIDE`/`PAGE` imports are gone with it — one of the path's two unsafe reconstructions, and the `Dma` view it rebuilt on the panic path. THE ACCOUNT IS SEALED PER LINE, AND THE CONTROL FAILS WITHOUT IT. The kernel's `Committed` writer committed on every `core::fmt` fragment, and one `writeln!` is several: a machine ending between two of them sealed a torn line under a valid checksum, which the next boot reads as a complete account. It is deleted for `toyos_blackbox::Account`, which seals only when the fragment it was handed ends the line, and leaves anything past the last newline on the page outside the sealed length. It lives in the crate because `Committed` was constructible only in the kernel and nothing could drive it: `an_account_cut_off_mid_line_is_readable_to_the_last_whole_line` reds under both mutations — the commit removed, and the commit moved back to per fragment — and counts the write-backs, because one line built from three fragments must seal once. The test that was named as the control last round and passed on the base as well is deleted. THE SWEEP RESTORES WHAT IT CHANGED. `sweep_under_load` raised the preempt count, armed a one-shot and set `IF`, then returned on two paths into `quiesce`, so `drain_all`, `sync_all`, `flush_disks` and `wait_for_durable` ran on that CPU with the preempt count raised. An `Interruptible` guard establishes the three and its `Drop` puts them back on every path; whether the caller arrived with `IF` masked is read before the `sti` rather than assumed. Also: the registered name is `usb_reset_records_the_phase_it_cut`, which is what it now asserts — the reset does not finish the command, by this branch's own argument — renamed at all six sites in this commit. The tracker entry is a `defect` and not a `finding`, because a fold would lose the three stick kills; its slug no longer carries a count the next boot moves, and the one citation to it is written in the same commit, in `stop.rs`'s header where the subject lives. The `UNMEASURED` marker and `Tier::Fast` stand, and the branch is still not landable on them: `src/durations.rs:293` refuses a committed marker, `src/tiers.rs:1520` refuses the marker on a Nightly row and `src/tiers.rs:1570` refuses a Nightly row with no CI evidence, and 63 s can never be Fast against `FAST_CEILING_MS`. One CI cycle on this pull request produces the measurement; a second commit prices the name and re-tiers it Nightly. Gates, each with the command's own exit status: `cargo run -- --clippy` 0 (5 invocations clean); `cargo run -- --build-only` 0; `cargo test --lib` 0, 298 passed; `cargo test --workspace --exclude toyos-build` 0; `cargo test -p toyos-blackbox -p toyos-xhci` 0, 26 and 53 passed; the renamed guest arm `usb_reset_records_the_phase_it_cut --nightly` 0, PASS 64 s, and `usb_reset_hands_devices_back --nightly` 0, PASS 39 s. The commit-per-line control both ways: `Account` sealing every fragment 101 (the torn line reads back, and one line of three fragments seals nine times where three lines owe three), `Account` sealing none 101 (the page carries the report and no account); `ACCOUNT_BYTES = 0` 101. Restored, 0. An earlier run of the guest arm exited 2, invalidated by a 994 s host suspend, and is not quoted as a result. No T14 boot: the bench's stick is dead awaiting a physical replug. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The third review returned LAND with six CODE and five PROSE findings. Every one
of them is dead weight or a decision nothing exercises, most of it added while
answering an earlier round, so each is answered by deletion rather than by an
edit or by a test bought to cover it.
The guard around the load sweep is gone. `Interruptible` established preemption
off, `IF` on and the one-shot armed, and put them back from a `Drop` that no arm
ever ran: the QEMU arm refuses on the disk floor before the guard is taken, and
the metal arm never returns from the sweep. The three statements and the three
that undo them are now in `sweep_under_load` itself, and the disk-error path
breaks instead of returning, so the function has one exit and the restore is
read against the loop that needed it. Neither the order of the lines nor which
of them a boot writes changes, so `usb_load_chain` judges exactly what it did.
The two-phase refusal is gone with its constant, the constant's copy in
`bootlog`, that copy's row in the source scan, and the predicate over it. It
refused an image that armed two `usb-wedge-*` names; nothing arms two, since
`one_wedge_phase` runs one arm per boot, and a kernel that staged the wrong
phase is already caught by name — `usb_wedge_chain` demands the account name
the phase that boot asked for. The refusal answered a question the judge
answers, and the predicate `says_nothing_of(kernel, USB_WEDGE_TWO_PHASES)`
stayed green on a kernel that never wrote the line at all.
`WEDGE_PHASES` spelled `toyos_xhci::bot::Phase::named()`'s three words a second
time, in the file whose neighbouring predicate had just been changed to stop
doing that. The table carries `Phase` values now and the judge formats them
through the `Display` that is `named()`, so the harness spells no phase word:
`git grep "data (unqueued)" -- tests/ src/` exits 1. It reads the declaration
rather than a fourth copy held to it by a scan, which makes a reworded phase a
type error; the dev-dependency that allows it is the arrangement `toyos-tco`
and `toyos-logstream` are already there for.
`Report::commit` had no caller outside its crate and is private. The variant
`NotOnTheRing::Unaligned` had no caller that could produce one: the single
caller's base is `block_device + {0, PAGE}` off a block at a multiple of
`MSC_STRIDE = 16 * PAGE` in a pool of whole 2 MiB pages, so every base is a
multiple of sixteen. The variant, its `Display` clause and its test go; the
check itself is folded into the `Dequeue` refusal, because dropping it would
have let a skewed base round to a plausible index, which is the one thing this
module exists not to do.
The section appended to another worker's issue file is reverted. What it
recorded — an account written into the page and never sealed — is what
`toyos_blackbox::Account` and `ACCOUNT_BYTES` fix on this branch, so it is not
open work and gets no file of its own. Measured off the pages run 43 and run 45
left: the reports ran to 13,773 and 13,763 bytes of the 14,296 a report may
spend, leaving 2,571 and 2,581 free, and the account was in those bytes with no
length or checksum over it.
The five prose findings are deleted and none is rewritten. What went: an earlier
implementation's arithmetic narrated in a test doc; an earlier failure offered
as a predicate's justification; "4 MiB", a count the code beneath it
contradicted, with no corrected number put in its place; `Account`'s
restatement of what `commit` already says at the site that seals; and the
narration of `deadline::this_cpu`'s implementation. The three pre-existing
lines the review passed by are untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The transport recovery builds on the Bulk-Only phase record that branch publishes, so its commits are taken whole rather than re-implemented beside it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ightly Run 34873852892's `durations` job refused with "committed UNMEASURED profile marker(s) are provisional and may not land: usb_reset_records_the_phase_it_cut" — its one bought Fast-tier run priced it at 64,260 ms (shards=12) in that run's own `test-durations-merged` artifact, six times over FAST_CEILING_MS. `tests/test-durations` carries that measured value instead of the marker, `tests/toyos.rs` moves the row from Tier::Fast to Tier::Nightly, and `src/tiers.rs`'s RELEGATED gains its row (Why::TimerAnchored, beside its sibling usb_reset_hands_devices_back): each of its four boots waits out a real per-phase deadline, so a faster machine does not shrink it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…GREEN Seen in the same session's fast-tier run, host carrying two other worktrees' suites throughout: smp_failed_ap_leaves_no_hole (spawn_init: WouldBlock) and log_reserve_window (root disk missed its 2000 ms budget, so the GPT scan found no ROOT partition). Both PASS run alone immediately after. Neither is touched by this branch; filed rather than chased, and filed separately from issues/build/parallel-tests-red-under-other-suites.md rather than appended to a file this agent does not own. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ort whose recovery is spent `usb-bad-cbw` corrupts the signature of three CBWs in a row on the disk the gate designated. QEMU's `usb-storage` answers each as BOT 1.0 §6.2.1 requires of a device handed an invalid CBW — a STALL of the Bulk-Out pipe — and answers every well-formed command as before, so what the boot measures is the driver's own conduct once its recovery has been spent on a device that is otherwise fine: three breaks running, one Reset Recovery after each, and then whatever the give-up does. The gate arms exactly the driver's own budget, immediately before the read it lands on, and reads the disk twice after: the read the stalls were staged on and the one after it, with the disk's health. `usb_transport_break` gains that boot as its second half. What it asks first is the behaviour — a device the driver said was not coming back is taken offline, its port reset, its slot given back, and nothing sent to it after — and only then the accounting. On the driver as it stands the boot reads `broke 3 times running; the transport is not coming back on its own` and then `read refused=true the read after it refused=false healthy=true`: the disk is left online and asked again, which is the storm the T14's run 55 spent its stick in. Measured red on this tree, twice, before the change that greens it. The break line names its completion code the way `Completion` does everywhere else — `code 6 (Stall Error)` rather than `code 6` — because the T14's black box printed `completion code 3` and `completion code 4` and the reader had to look them up. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…, and one that stays broken is taken offline The T14's stick (SanDisk Ultra 0781:5581, slot 5 on 00:14.0) hung four boots in the same shape, and run 55's black box finally carried the shape: a READ(10)'s status phase answered with a data packet (Babble Detected, code 3, Bulk-In halted), the next CBW answered with no handshake (USB Transaction Error, code 4, Bulk-Out halted), alternating every seven milliseconds; after every third break `scsi` said "the transport is not coming back on its own" and left the disk online, so the caller's deadman asked again and the cycle ran until a Set TR Dequeue went unanswered and the disk was declared offline with the device still inside a command and one pipe halted. Run 24's `ccorpus` log holds the first break of the same shape whole: a CSW two seconds late, both endpoints Running, abandoned. Three things in the recovery were wrong against the specifications it cites. Reset Recovery cleared the halt on one pipe. BOT 1.0 §5.3.4 is a Bulk-Only Mass Storage Reset then ClearFeature(ENDPOINT_HALT) on the Bulk-In and on the Bulk-Out, unconditionally; the driver sent the clear only where the controller had reported Halted, so the other pipe's device end was never touched. And a ClearFeature(ENDPOINT_HALT) zeroes the device's toggle or sequence number whether the endpoint was halted or not (USB 2.0 §9.4.5, USB 3.2 §9.4.5), which is why the clear cannot be sent without the host zeroing its own: Reset Endpoint does that from Halted (xHCI 1.2 §4.6.8) and only a Configure Endpoint with Drop and Add set does it from anywhere else (§4.8.1). The recovery is now `toyos_xhci::reset_recovery`: Reset Endpoint or Stop Endpoint per endpoint as its state permits, one Configure Endpoint re-creating both endpoints Running on fresh rings, then the class's three requests. Every command before the first request, which is the ordering the old `Owed` split existed for; the pure crate's tests hold it for every pair of states. The retry budget started over on every operation. Three attempts were "the fault, the answer the device still owed, a clean run", but the count was per `scsi` call and the caller's own deadman asked again for two minutes: a device that never came back was asked, recovered and asked again some hundred times. The count is per device now, cleared by a completed round trip, and the third break running is the end. The give-up left the device where the last break found it. Nothing past §5.3.4 was ever taken, so an offline disk sat inside a command with a halted pipe until the platform reset, on a laptop whose ports never lose power. `take_offline` writes PORTSC.PR, which returns the device to its Default state and abandons the command (§4.19.5, USB 2.0 §9.1.1.5), then Disable Slot (§4.6.4), and leaves the port attached with no slot as a HID device let go is, so nothing enumerates it again every debounce and no command reaches it. A port that reads disconnected is not a broken transport: the pull's own teardown owns that device, and a recovery or a reset aimed at an empty port only spent their bounds. Two smaller things the record asked for. The break line names the completion code the way `Completion` does everywhere else, so "code 3" reads "code 3 (Babble Detected)" on the one channel the T14 has. A command that times out now prints USBSTS and CRCR.CRR beside the silence, because run 55's "Set TR Dequeue timed out" said nothing about which of a stopped command ring or a Host Controller Error it was. `usb-bad-cbw` corrupts the signature of three CBWs in a row on the gate's disk, which QEMU's `usb-storage` answers as BOT §6.2.1 requires — a stalled command phase — on a device that answers every well-formed command. `usb_transport_break` gains that boot as its second half: recovered twice, offline on the third with `reset=true, slot disabled=true`, nothing sent to the device after, `healthy=false`. On the base the same boot reads `healthy=true` and the disk is left to be asked again. Two findings filed rather than fixed here: the first break in both records is a CSW two seconds late, and the abandon that follows is what loses the phase (`a-stick-that-answers-late-is-broken-by-the-two-second-abandon.md`); and offline is the end today where the protocol has one rung more, a device reset that keeps the disk (`a-disk-taken-offline-is-never-brought-back.md`). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…tate that moved, and the wire judges the three requests Round 2 of #466, answering the review of b2e4f0a. Take-offline. It found its port with port_of_slot, which is None for a slot mid-enumeration, so a stick that broke three times inside its bind got no PORTSC.PR; it then disabled the slot itself and bind's refusal disabled it a second time; and it issued Disable Slot with one endpoint Halted or mid-transfer, which xHCI 1.2 section 4.6.4's note calls undefined. Now the port is the device's own port_idx, both endpoints are brought to Stopped first, and the slot goes back through the one Disable Slot site the driver has (submit_disable_slot/slot_gone): from the poll for a bound disk (give_back_offline_slots, since a disk operation runs beside whatever is outstanding and that site takes one operation at a time), and from device::refuse for a disk still inside its bind, whose port holds no slot yet. A pair that cannot be Stopped keeps its slot until the unplug. The state machine. The plan read both endpoint states once and issued the commands chosen from that read; a transfer that errors in between turns Running into Halted, the Stop Endpoint answers Context State Error and the disk went offline on a race. reset_recovery::quiesce now yields one command per look and is asked again after every answer, MOST_LOOKS = 5 times at most, and the pure crate's test walks every pair against section 4.8.3's state machine with either or both pipes erroring under the look. Error takes its section 4.6.10 rung (Set TR Dequeue Pointer) in both sequences. Owed, quiesce_endpoint and the dead Owed::Failed reason are gone: clear_endpoint_halt takes an address. The reconfigure copies its slot context from the controller's output context, so MscDevice no longer carries a speed for a field the command ignores. The log. A completed round trip that ends a run of breaks says so where the count is cleared ("completed after N break(s) running"), whichever call it happens in: a 2000 ms silence spends the operation budget, the call returns Scsi::Budget, and "completed on attempt N" could never print for it. A recovery whose every step answered says so, and the reconfigure prints the two endpoint states the controller's output context holds after it. The tests. The gate stages, under usb-transport-faults (was usb-bad-cbw), one break short of the budget by a bad CBW signature (Bulk-In Running, Bulk-Out Halted in QEMU), the same count by a withheld CBW (the pair the other way round), then the whole budget, then the whole budget on the INQUIRY of the next disk to bind; usb-port-gone stages a read whose port reads gone. The second run only passes if the first run's completed read cleared the count. The three requests are judged on QEMU's own pcap of the disk's traffic (BootOptions:: usb_pcap): every Bulk-Only reset on the wire is followed by ClearFeature (ENDPOINT_HALT) to the IN endpoint and then the OUT endpoint. The budget is read off the gate's line. A staged fault is disarmed after the read it was staged for, and the gate prints how many were never taken. usb_transport_break gains a metal arm, usbbreak: tests/jobcase armed usb-transport-break, the staged break on the boot stick itself. Measured by hand, once, since no committed test makes a command time out: with the doorbell withheld for one Stop Endpoint (a local edit to command_code, restored; the working tree's diff hashed the same before and after), usb_transport_break's first boot printed [kernel 2.202 cpu0] xHCI: Stop Endpoint timed out after 2000 ms with USBSTS=0x00000008 and CRCR.CRR=0 Citations corrected to the sections that say it, as the review read them off the fetched specifications: BOT 1.0 section 6.6.1 for an invalid CBW (the Bulk-In STALL is the mandatory half; QEMU does the optional Bulk-Out one), xHCI 1.2 section 4.6.8's note for Drop and Add, section 4.19.5 for what PORTSC.PR leaves. USB 3.2 section 9.4.5, USB 2.0 section 9.1.1.5 and xHCI section 5.4.8 are deleted: unread, wrong, and unverifiable in that order. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
`usb_transport_break`'s metal arm is `tests/jobcase` armed
`usb-transport-break`, and the suite refuses to stage a boot whose job list
`tests/metal-profile.toml` does not price ("the boot "usbbreak" runs 0 job(s)
and list.usbbreak.job_ms prices none of them"). Six rows beside `jobcase`'s,
with `jobcase`'s ceilings and no `measured`: no run has taken this boot.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The branch was behind main by #457 and #463; neither touches the xHCI driver, the pure xHCI crate or the USB tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…get after a break, and every piece of the recovery has a judge Round 3 of #466, against the second review. Four pieces of round 2 changed nothing any committed test read; each now reds one, or is gone. The input context is the pure crate's (toyos-xhci/src/configure.rs). The driver wrote the Drop flags, the Add flags, the slot context and both endpoint contexts itself, and QEMU re-creates an endpoint on an Add whatever the Drop says, so a recovery with Add alone passed everything. Every dword is now decided by `configure::bulk_pair` and handed over as a list the driver writes without looking inside; the driver has no flag left to omit, and the crate's tests read the Drop flags, D0/D1, the slot context field by field and the endpoint contexts against numbers worked by hand. The slot context is the specification's. Round 2 copied all four dwords out of the device's output context and cited xHCI 1.2 section 6.2.2.2 for it. That section asks a Configure Endpoint's input for Context Entries and the Hub fields, and Table 6-7 has USB Device Address and Slot State initialised to 0 as input; the copy carried both non-zero. The recovery's slot context is now Context Entries with everything else 0, and the sentence is deleted. The quiesce is `reset_recovery::Quiescing`, and it keeps the image section 4.8.3's note tells software to keep. A transfer event's completion code is where a pipe starts: Stall, Babble, USB Transaction and Split Transaction Error are Halted, TRB Error is Error (`event_state`), and `Broke::Code` now carries its pipe so `scsi` can hand it over. A command that succeeded leaves its pipe Stopped whatever the field goes on reading. A Context State Error rules out, for that pipe, the state the command was chosen from: a field that moved is believed, and one that still reads the ruled-out state is taken to lag, so the plan goes on to Halted and then Error, the two states section 4.6.9's note says a Running endpoint reaches by itself. The old rule re-read the same field with no delay and spent its five looks in microseconds on a controller that defers the write, taking a healthy disk offline; the model's walk now has a field that never updates, and `a_halting_completion_code_outranks_a_field_that_still_reads_running` reds a plan chosen from the field. The bound moves from 5 looks to 7 and is still asserted to be the worst case, not a margin. What a Context State Error means, what any other code means and what silence means are all decided in the crate; the driver's loop issues what it is told and ends where the plan ends. One budget for the whole call (toyos-xhci/src/call.rs, `CALL_AFTER_BREAK`). A controller that left a command unanswered was sent another by `take_offline` and then had a port reset waited for: up to four 2 s spins in one call, under `XHCI` with preemption off, against a TLB-ack tripwire of 5 s. The budget opens at the start of the bulk wait that broke and is 4 s, twice the driver's one timeout; every later wait of the call (commands, control transfers, the reset's settle) is clipped to it, a step reached with nothing left is not taken and says so, a command that got no answer ends further commands to that controller in that call by name, and a command is not re-issued into a spent budget, where every wait would be cut at once and counted against the device. A const assertion holds the budget under `tlb::ACK_TIMEOUT`, whose comment now names it. `scsi` closes the budget on every way out; the ready loop's break opens and closes its own. Where a slot goes is one decision (`reset_recovery::slot_after_offline`), for a bound disk and for one inside its bind. `bind` answered `false` and `device::refuse` issued Disable Slot whatever the quiesce had said, so the section 4.6.4 precondition was dropped on the bind path exactly when it had failed. `bind` now answers `Bind::Refused(SlotGoes)`, and a pair that could not be Stopped leaves its slot with the port, whose teardown gives it back. A TEST UNIT READY whose recovery fails goes offline by the same path as any other command instead of setting `failed` and falling into `refuse`. The INQUIRY faults are staged by the bind that takes them. The gate armed them for a later bind and nothing ever disarmed them or printed what was left. The gate now only says how many the next bind should stage (`stage_bind_faults`); `bind` arms them before its first command, disarms after `bring_up` and prints `untaken=`, which the test asserts is 0. The test asserts the give-back it described. The offline disk's `slot N disabled` line has to come before its port's `disconnected` line: with `give_back_offline_slots` never called the unplug's teardown gave the slot back and the log read the same two lines. The issue about the missing device-reset rung states it from xHCI 1.2 section 4.6.11 and names no other driver. Prose the review refused is deleted: "runs on a faulting thread", "this driver has two", "which is exactly what the T14 printed twice", "its unplug had none left to give". Not judged by any committed test, only by reading: that `scsi` passes `broke.event()` rather than `None`, that `device::bind` acts on `SlotGoes::WithTheUnplug`, and that the two break sites open the budget. The decisions they feed are tested in toyos-xhci; QEMU models no lagging field, no silent controller and no quiesce that fails. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… for a wrapped difference `settles_within_call` computed `ends - now`, and once the budget had ended `ends` was behind `now`: the subtraction panicked the kernel inside `take_offline` (`src/drivers/xhci/wait/mod.rs:150:31`). Found by the hand probe that withholds every doorbell of one read, on its first run, on e7e0042 plus the uncommitted probe patch; no committed test reaches a spent budget in the kernel. The arithmetic is `AfterBreak::wait_left`'s now, saturating, and the pure crate's run of a silent controller asserts a settle started at the budget's end, and one started a second after it, both get 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Filed from round 8's gate: 7168 + 2832 bytes, green alone and green on the next whole fast tier of the same head. Not this branch's to fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…e USB transport branch One conflict, `tests/metal-profile.toml`: at the same place this branch added the `boot.usbbreak.panel_max_us` and `boot.usbbreak.panel_us` ceilings and main added `boot.lancase.panel_max_us` and `boot.lancase.panel_us`, each a four-line block with the same unit, ceiling and source. Both blocks are kept whole. The rest merged by itself. `cargo run -- --build-only` exit 0, `cargo test -p toyos-build --lib` exit 0 (293 passed, 1 ignored), `--nightly usb_transport_break` exit 0 on the result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
|
Review at Readiness. PR CI at Earlier BLOCKERs. The round-2 review is not on the PR. Its four surviving mutations (G1, R1, C1, R3) are recorded as killed at
Mutations (
BLOCKER
NOTE
REMOVE
SEND BACK |
…ever where the device binds Answers the round-8 review of #466 (four BLOCKERs, four NOTEs, REMOVE). The IF-clear bound (BLOCKER 1). `served` held a call for a device a reset of this driver's had moved and, on the caller's CPU with IF clear, stepped the ports itself: the enumeration and the bind of whatever arrived (TEST UNIT READY loop, INQUIRY, READ CAPACITY, two string reads) ran inside the wait, and the re-issue after `Back` opened a second full `AfterBreak`. Now: - the operation is one call: `AfterBreak` is carried by `served` across every command, the hold and the re-issue, and closed there (the bind closes each of its own commands); `scsi` no longer closes it per command. - `toyos_xhci::call::AfterBreak::hold` makes the hold part of the call: it opens the call if nothing broke, ends where the last rung begins, and the re-issue is clipped there as after a port rung that took. A path walk over every sequence of rung entries, rungs taken, holds and spins (8^6 paths from three starts) asserts each ends inside the bounds' sum. - the hold only waits for the verdict: it looks with `try_lock` (a bind may hold the lock for seconds), asks for the ports to be stepped (`PORT_WORK_AT` due now) and kicks every other CPU once, since the connect's interrupt may be the held CPU's. The lock is taken again for the re-issue only before the hold's end. - the Gone arm opens the call from the wait that saw the port go. Power cycle against move (BLOCKER 2). A device that comes back is not known to have kept its power. `MscDevice::unflushed` is set by a WRITE the device reported complete and cleared by a SYNCHRONIZE CACHE that succeeded; a disk held while `owes_a_flush` (unflushed and not `no_write_cache`) is taken back with its next flush failing by name, once. `Late` (BLOCKER 3) is `toyos_xhci::late`, pure, with a test that a ring that keeps producing is given up on one ring's worth of reads past the deadline. `usb_reset_records_the_phase_it_cut` (BLOCKER 4). Not this branch's ladder: main's 5213b6e ("The loader files the record's tail instead of scrolling it") moved the black box's ring tail off the console into loader.log, and the wedge's own records (`usb-wedge: stopping every CPU at the`, `wedge: staged`) are tail. The account (the page's head) still named every phase. The QEMU half now boots a kept image and reads the wedge's records off loader.log, as `boot_deadline_ends_a_wedge` does since that commit. NOTEs: - `ladder::left(phase, data_out)`: a command-phase break of a command with a Data-Out phase is a device owed data, so it enters at the port rung. - the race between `with_disk` and the first look: the disk found here after an operation that could not find it, or that saw it go held, is back. - a disk refused not ready, or for want of a pool block, while a disk on its controller is held is enumerated again (`AfterSlot::Again`) instead of left refused; ports that read empty are serviced before connected ones, so a teardown frees its block before an enumeration elsewhere claims one; and a held disk is not forgotten while an enumeration is under way, so a returning device's own bind (its whole READY_BUDGET) does not make it late. - `usb-serial-short` asks for the serial string in 8 bytes; the first `usb_transport_break` boot requires every disk to say `named and not read`. Tests: `usb_transport_break` boots nine times; new are `Moved::SlowStick` (`usb-slow-return` stalls the returning stick's bind 2.5 s: the break's call ends holding, inside CALL_AFTER_BREAK, on another CPU than the bind, and the disk is still taken back) and `Moved::OwedFlush` (`usb-transport-break-owed`: taken back, next flush fails by name). Every moved boot that comes back asserts the held call ended within 4.75 s of the break and that the bind ran on another CPU. REMOVE: run 79's provenance on RETURN_WINDOW (0.98 s from the reset at 1.897 s to the stick bound on port 13 at 2.878 s, twice that is 2 s) and the `T14 run NN` citations in ladder.rs and call.rs are deleted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The LATECAP mutation (no cap past the deadline) made the helper loop for ever instead of going red. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…he stick on a cue the rung writes itself Two defects the moved-stick boots showed in five runs of usb_transport_break at 99a81a8. Every CPU inside a call on the held disk (utb-rateb2: cpu0's write held, cpu1's read of the same disk held) takes no scheduler pass, so the stick that came back on port 3 at 0.356 s was not enumerated until both holds ended at 2.354 s and the window had passed: disk 0 lost, the boot's reads failed. The window is the device's to arrive inside, not a CPU's to get round to: a held disk is not forgotten while a port reads connected and the port machine has not taken it, as it was already not forgotten while an enumeration is under way. The judge now takes both shapes the ruling allows: the held call sees the stick bound and sends its write again, or it ends on its bound (the stall, or every CPU held) and the write is asked again; a bind the CPU check applies to is one that happened while the call held. The host moved the stick when the HELD record reached the console, which is klogd's to drain; with klogd not running while the rung spun, the cue arrived after the rung's 1.5 s and staged a device that left too late (mut OWEDNOT and utb-rate2's SlowStick: "Reset Device not issued: the bound on this part of the call is spent", offline). The staging now writes its cue to the console itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…a call on it Found by usb_transport_break's moved-stick boot in round 9 of #466. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
Clippy's redundant_clone in `cargo run -- --clippy` at 9b57729. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
|
Round 2 review at Readiness. PR CI at Round 1 BLOCKERs
BLOCKER
NOTE
REMOVE
SEND BACK |
…e staged, and a re-issue after a hold is timed B1: `usb-return-silent` stalls the returning bind 1.5 s and leaves every transfer of the operation the held call sends again unanswered, each wait spun to its end. `usb_transport_break`'s SilentReturn times that call from the break: it must end inside `AFTER_BREAK.whole()` and not before its waits reach where the last rung begins. A re-issue on a bound of its own (the review's REISSUEFRESH) runs past it. B2: the flush debt is `toyos_xhci::flush::Debt`, pure. A debt carried from the instance before is owed again, so a disk adopted owing a flush that leaves before any flush hands it to the next instance, whose next flush fails. Tested two hops deep. B3: `usb-transport-break-flushed` breaks the first write after a flush that succeeded over the one before it, kept apart from the driver's own debt; FlushedStick must adopt owing nothing. The crate tests both edges too. The moved-stick judge now reads the call its transport broke in off the break's own CPU: a call on another CPU that found the disk held ends its own, and FlushedStick's boot measured one such at 14 s. NOTEs: `AFTER_BREAK` is declared once, in `toyos_xhci::call`, which the test reads; the kernel asserts its wait is `USB_TIMEOUT_NS`. `ports_wanted` says when a kick is refused. The held-disk issue carries `read_block_retrying`'s spin across calls. The T14 run citations in the moved-stick judge are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ed before its order SilentReturn's held call sees the stick back only if a CPU in no call on the held disk binds it. One run of REISSUEFRESH's two put both of a two-CPU guest's CPUs in calls on disk 0 until the call's bound, so the operation was never sent again on it: the held-disk issue's shape, which this staging cannot measure through. FlushedStick under NEVERCLEAR2 was red first on the missing reset rather than on the debt it came back with; the lines no stick that came back as itself may write are now read first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…other line FlushedStick at two CPUs took the held-disk issue's shape in every one of five runs: a write and a flush retried on both CPUs until the job reset, and in one of them the reset came before the stick was bound, which the judge's order refused. Four CPUs bind it inside the held call. Its debt lines are read before the block layer's failure lines, so a stick taken back owing a flush is red by that name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…ecovered READ is sent again T14 run 84: the stick on port 13 was enumerated on the link the firmware trained, with no reset; its first READ(10) was not taken for 2 s, the class reset's TEST UNIT READY babbled, the port reset took, and the READ was then refused because the first wait had spent the operation's 2 s. - The boot scan resets every device it finds connected: a trained USB3 link is warm reset before anything is asked of its device (`toyos_xhci::port::inherited_reset`). HCRST drives no bus reset, and SET_ADDRESS to a Configured device is not specified. Hot-plug is unchanged: a link this kernel watched train came up from power-on. - The command a rung that took sends again is the call's to decide, not the operation's (`toyos_xhci::call::AfterBreak::issue`): the operation budget gates only a command that starts something new. Run 84's timeline is a pure test on the kernel's own bounds. - Staged under QEMU: `usb-inherited-data-in` has this kernel play the firmware (bind, a READ(10) whose data nothing reads, forget) before the scan; `usb-first-wait-spent` stages a READ whose wait spends the whole operation, a class reset out of step, and a port reset that takes. Both judged by `usb_transport_break`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…reset as a firmware leaves it The first staging forgot the device by stopping its endpoints and giving its slot back, and on that path QEMU's usb-storage came back out of the data-in: with the reset at bring-up reverted, its first command was still answered, so the staging measured nothing. Now this kernel plays the firmware on a bring-up of its own, leaves the slot and the data-in as they are, halts the controller, and its own bring-up resets the controller (HCRST, which drives no bus reset) before the scan, as on the T14. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…s the device
QEMU 11.1.0's `xhci_address_slot` calls `usb_device_reset` before it
delivers SET_ADDRESS ("Make sure device is in USB_STATE_DEFAULT state"),
so under QEMU every enumeration resets the device whatever the driver
did before it. Both stagings measured that and nothing else: with the
reset at bring-up reverted, a device left inside a READ(10)'s data-in
answered its first command anyway (the second staging's double bring-up
also lost the controller's command answers after its IOMMU domain moved,
a path no production code takes). No QEMU boot can red on this defect;
it is judged by the specification and the T14.
`port::reset_needed`'s doc says who asks it now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…e tracked The first READ(10) broke on a stick whose TEST UNIT READY, INQUIRY and READ CAPACITY had each just completed in step, and a warm port reset that took was followed by a READ whose data did not come: the late-stick issue's third record. And the stick was lost with every path ending on a reset and no command open: the lost-stick issue's case that nothing cut. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… tripwire Found while redesigning the re-issue's budget: `CALL_AFTER_BREAK` bounds the spin from the wait that broke, and an operation's earlier batches spin before it with the same lock held. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
… before it is set T14 run 86: port 13's warm reset was judged in the millisecond it was asked for. PORTSC read 0x002212b1 — PR still set, the link in RxDetect — and the PRC beside it was a flag nothing had cleared, left by whatever reset the port before this kernel ran. The scan called the link untrainable, skipped the port, found no disk and panicked in `rootfs.rs`. - `Portsc::reset_finished` is the completion rule: PR back to '0' and PRC or WRC set (xHCI 1.2 §4.19.5, §4.19.5.1). Every wait for a reset — the boot scan's, the port machine's, the ladder's port rung — asks it. - `port::reset_write` clears the reset-finished flags it saw, so the flag that comes up next is this reset's. The ladder's own acknowledge, which did this by hand, is now that one write. - `a_warm_reset_is_finished_only_once_the_controller_has_cleared_pr` walks run 86's word and run 84's completion word on the same port. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
|
Round 3 review at Readiness. PR CI at Round 2 BLOCKERs
Round 11's own negative controls re-measured here: PRCONLY ( BLOCKER
NOTE
REMOVE
SEND BACK |
|
Round 3's BLOCKER is closed by a bench boot, not by a change: T14 run 90, the head The loop reports The review is right that runs 88 and 89 did not take that path: their stick trained on the USB2 half at 🤖 Generated with Claude Code |
Review round 3's NOTEs, and its REMOVE of the four `T14 run 86` / `run 84` citations in `toyos-xhci/src/port.rs`. The invariants they annotated stay: the words in `a_warm_reset_is_finished_only_once_the_controller_has_cleared_pr` are unchanged, and what they mean is said without a run number — a reset judged in the millisecond it was asked for reads as a link that would not train, and the completion word that follows it is the finished reset. Where those words came from is the commit that added them and the pull request. `init_device` asked `port::reset_needed` a second time only to pick a log string. It now asks once and acts on that answer: `port::inherited_reset` takes the answer rather than the port, so the `None` a trained link produces is read in one place and the boot scan's log line and its reset kind come from the same read. The decision is unchanged — a trained link still gets a warm reset before anything is asked of it — and no caller of `reset_needed` sees a different value. With that, `reset_needed`'s header is true as written again: it is the one place the question is answered, and `inherited_reset` only says what its `None` costs at bring-up. `Portsc::reset_changed` is private: nothing outside `portsc.rs` asks it, and `reset_finished` is the question a caller has. `cargo test -p toyos-xhci` EXIT=0 (111 passed). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
…uld not start Run 35760805511 attempt 1 redded `log_reserve_window` on shard 9 and `log_nested_emit` on shard 10, at this branch's head and at no point of main. Both are one shape said twice: a process that would not start. The first guest's `test-runner` and `soundd` each faulted on the first instruction of its own image with the PML4 entry for the user half absent, on two CPUs in the same millisecond, each with its own CR3 and PCID; the second guest's log gate got `Err` from spawning `/system/bin/echo` and printed `other error`, which is what our std gives an errno it has no kind for. Whether either is this branch's was measured rather than argued. On this head, four full fast-tier sessions on the dev host: both names green 4/4. With the whole change reverted onto `cf715c49` as a checked patch — applied under a trap that restores it, the tree read clean afterwards, 52 files and 8,781 deletions, `git diff cf715c4` empty with it applied — four more sessions of the same suite: both names green 4/4. The base arm ran `cargo test --test toyos-build` rather than the whole of `cargo test`, because a patched revert leaves `kernel/src/log/recovery.rs` tracked and absent and `sourcegate`'s walk reds on exactly that; the guest suite is the same binary either way. So neither arm reproduced either name, and neither arm acquits the branch: 0/4 against 0/4 is an instrument that does not reach a failure a KVM shard produced with eight guest vCPUs on a four-CPU runner. What the files carry is the evidence, the arms, and what a next sighting has to capture. No quarantine row is proposed: that is the orchestrator's call and needs a `says` fragment. The branch's kernel diff outside the USB paths is additive — `mm/dma.rs` gains two functions, `arch/tlb.rs` a doc comment and a visibility — and touches no part of spawn, address-space construction or TLB shootdown. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK
The T14's stick hung four boots the same way, and run 55's black box carried
the shape. This branch makes the mass-storage transport recover on a ladder —
the class's own Reset Recovery, then a port reset that keeps the disk, then
offline behind one last reset — each rung ended by the device's own answer and
each on a bound of its own; and since round 7, a disk whose device a reset of
this driver's moved to another port takes its number back when the same device
binds there, so the volume on it carries on. Since round 11, every device found
connected at bring-up is reset before this kernel asks it anything, and the
command a rung that took sends again is bounded by the call, not by an
operation budget the break already spent.
Not armed. Round 6's ladder met the owner's ruling on the T14 for the first
time (run 79: no replug). Round 7 survived a staged break twice there (runs 80, 81); Round 8 fixes the wait that redded PR CI. See Round 11 first: T14 run 84, the first real failure, and what it does and does not show.
Builds on #447, which is open and not merged: its head
78929f12is in thisbranch as
7fd707c3.origin/mainat2fbfdf9d(the governance cut, #475) ismerged in as
7f4155b1: two conflicts, both main's rewrite —src/tiers.rstaken as main's (its
RELEGATEDis gone; the branch's two names stay Nightly bytheir registration word in
tests/toyos.rs, where a comment citing the deletedvalidate_ci_profileis cut), andtests/test-durationstaken in main'scolumn-less format with the branch's one row
(
usb_reset_records_the_phase_it_cut 64260) added in it.Round 12: the review of
0a6b7da5answeredCommits
e45c56f5(the NOTEs and the REMOVE) andcec68125(two issues).origin/mainiscf715c49, already in this branch; the merge was a no-op.Nothing a bench line shows changed: the boot scan's decision, its log line
and the reset it writes are what they were at
0a6b7da5, and the only editsare where a question is asked and who may ask it.
The round's BLOCKER is not answered here: the inherited reset is
bench-unproven, the T14 boot that judges it is the orchestrator's, and this
head is what it is booting. No image is staged by this round, and Round 11's
T14 subsection describes what was requested at
0a6b7da5, not a run.The NOTEs
init_deviceaskedport::reset_neededa second time only to choose a logstring. It asks once now:
port::inherited_resettakes that answer insteadof the port, so the log line and the reset kind come from one read. No
caller sees a different value, and the trained-link case is still the only
one the two disagree on.
reset_needed's header is true as written — it is the one placethe question is answered, and
inherited_resetonly says what itsNonecosts at bring-up.
Portsc::reset_changedis private: nothing outsideportsc.rsasks it.The REMOVE
The four
T14 run 86/run 84citations intoyos-xhci/src/port.rsaregone, and so is the sentence in Round 11's defect 1 that promised a T14 run
"below" this body does not carry. The test's words are unchanged — the same
four PORTSC values, asserted the same way — and what they mean is now said
without a run number.
The two CI reds nobody held
log_reserve_window("Init process crashed during boot") andlog_nested_emit("the record-making child would not start: other error"), both on attempt 1 of
run
35760805511at0a6b7da5, both green on theirALONEre-run, neitherquarantined, neither on main's CI at
cf715c49. They are one shape said twice:a process that would not start. Shard 9's guest had
test-runnerandsounddfault on the first instruction of their own images with the PML4 entryfor the user half absent, on two CPUs in the same millisecond, each with its
own CR3 and PCID; shard 10's log gate got
Errfrom spawning/system/bin/echo, printed asother error— the kind our std gives an errnoit cannot name.
Re-measured rather than argued, on the dev host, 2026-09-22:
log_reserve_windowlog_nested_emitcargo test(fast tier, 347 names)cf715c49, whole change revertedcargo test --test toyos-buildThe revert was the whole change as a checked patch (52 files, 467 insertions,
8,781 deletions), applied under a trap that restores it; with it applied
git diff cf715c49was empty, and the tree read clean after. The base arm ranthe guest binary alone because a patched revert leaves
kernel/src/log/recovery.rstracked and absent, which redssourcegate's walkin
cargo test --lib; the guest suite is the same binary either way.Neither arm reproduced either name, so neither arm acquits this branch:
0/4 against 0/4 is an instrument that does not reach a failure a KVM shard
produced with eight guest vCPUs on a four-CPU runner. Filed, not attributed and
not fixed:
issues/kernel/two-processes-started-at-an-entry-point-nothing-had-mapped.mdand
issues/kernel/a-spawn-of-echo-was-refused-with-an-error-nothing-names.md,each carrying its capture, both arms, and what a next sighting must capture. No
quarantine row is proposed — that is the orchestrator's call and needs a
saysfragment. For the record, the other reds those eight sessions produced were
blocked_dump(the panicissues/kernel/the-blocked-task-dump-panics-when-a-cpu-is-inside-inbox-submit.mdcarries), a quarantined
console_locale_detectstall,i8042_undecoded_bytesand — on the base arm —
metal_device_probe.Gates at
cec68125(tree clean before and after)cargo test -p toyos-xhcicargo test --test toyos-build -- --nightly usb_transport_break… --nightly usb_12 passed, 1 quarantined and green: usb_disk_index_stable… --nightly xhci_… --nightly blackbox_cargo test(the fast tier)347 passed, 8 quarantined and green … 347 total (179.5s), 104 held back for the nightly tiercargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildcargo run -- --clippyUnsure of
the dev host does not reach it.
Round 11: T14 run 84, the first failure nobody staged
Commits
b7fd5929(the change),a710dc21andd85d03df(a staging fordefect 1, reworked, then deleted: see below),
4959c4efanda07f021d(issues).
origin/mainatcf715c49is merged in as72e0a648, no conflicts.Run 84 (read by run 85's page):
port 13 link already trained, no reset neededat 0.595 s; the bind's TEST UNIT READY, INQUIRY, READ CAPACITY andserial string all completed in step; the boot's first READ(10) had
no answer in the command phase in 2000 ms; the class reset's TEST UNIT READY babbled;the port reset took and TEST UNIT READY answered under its own tag; the READ
was then
not issued: 2000msbecause the first wait had spent the operation;the next READ's data did not come in 2 s; offline; no boot partition; panic at
rootfs.rs:71; the firmware and Linux could not use the stick until it wasreplugged.
Defect 1: a device the firmware left is reset before anything is asked of it
Change. The boot scan resets every device it finds connected
(
toyos_xhci::port::inherited_reset): a trained USB3 link is warm resetbefore enumeration and says so (
xHCI: port N link already trained before this kernel ran; warm resetting it before its device is asked anything). USB2 portswere already reset. Hot-plug is unchanged: a link this kernel watched train
came up from power-on (
reset_needed, the sim'sa_trained_superspeed_link_is_not_reset).Why, from the specifications (recalled, not re-read: no spec text is in
reach of this worktree). xHCI 1.2 §5.4.1: HCRST drives no USB reset on the
root ports, so the controller's reset tells the device nothing. USB 2.0 §9.4.6:
SET_ADDRESS to a device in the Configured state is not specified, and that is
what an Address Device on an unreset, firmware-configured device sends. USB 2.0
§9.1.2: enumeration resets the port before it addresses the device. Warm rather
than hot because the link's state is as unknown as the device's, and a warm
reset is a hot one plus a retrain (xHCI 1.2 §4.19.5.1). Mature kernels do the
same: Linux's hub driver resets every port before it addresses the device on
it, a port it finds enabled when it loads included.
The hypothesis against run 84's own records: not borne out. At 0.596 s the
stick completed four tag-checked round trips, so its Bulk-Only state was in
step when the first READ went out, which a device still inside a firmware's
data-in would not be. And the recovery's own warm reset of port 13 took at
2.707 s, TEST UNIT READY answered, and the next READ's data still did not come.
What the records do show is a stick whose protocol answered every reset while
its media stopped at the first command that needed it: the reading
issues/kernel/a-stick-that-answers-late-is-broken-by-the-two-second-abandon.mdalready carried, and run 84 is now its third record. This fix removes an
unspecified request and does what the specifications and Linux do; it is not
shown to cure run 84, and it is bench-unproven at this head: no T14 boot has
run against it, and this body carries none. The orchestrator's boot of this
head is what closes that.
Cost. Under QEMU, nothing measurable: three boots of
xhci_superspeed_portseach way, head and reverted, all read
port 1 connectedandport 1 enabled, speed=4within 1 ms of each other andBoot: peripherals readyat 116–117 ms(QEMU completes a reset at once). On the T14, an estimate from run 84's own
warm resets of port 13: 105 ms each (2.602→2.707, 4.758→4.863 s), each
including the Stop Endpoints before it, so an upper bound per inherited USB3
device, not a boot measured with this change.
Not stageable under QEMU. QEMU 11.1.0's
xhci_address_slot(
hw/usb/hcd-xhci.c) callsusb_device_resetbefore it delivers SET_ADDRESS,"Make sure device is in USB_STATE_DEFAULT state": under QEMU every enumeration
resets the device whatever the driver did first. Two stagings measured exactly
that. With this kernel playing the firmware (a READ(10) whose data nothing read,
then the device forgotten) and the fix reverted, the scan's first command was
still answered; a probe TEST UNIT READY sent to the same slot before the
re-enumeration was stalled, so the staging did leave the device inside the
data-in, and the Address Device took it out. Both are deleted (
d85d03df).Negative control NOINHERIT (
init_deviceback toreset_needed, no resetfor a trained link):
--nightly xhci_superspeed_portsEXIT=1,"xHCI: port 1 link already trained, no reset needed" does not read "link already trained before this kernel ran; warm resetting it before its device is asked anything". That is the reset's own record, not a harm: no harm isobservable in QEMU, for the reason above. Independent oracle: the
specifications above and the T14. Pure:
a_device_found_at_bring_up_is_never_enumerated_without_a_reset.Defect 2: the command a rung that took sends again is the call's
toyos_xhci::call::AfterBreak::issue(now, operation_ends): the first commandafter a rung took, or after a held disk came back, is the one the break was
in, and only the call's window decides it; every other command starts
something new and needs the operation's budget too.
AFTER_BREAKandCALL_AFTER_BREAKare unchanged (4.75 s <tlb::ACK_TIMEOUT5 s). Thecommand sent again gets at least what the rung that took left of its own
window: asserted at every
tookinevery_path_through_a_call_ends_inside_its_bounds.(
a_read_whose_wait_spent_the_operation_goes_out_again_after_the_port_reset_took):operation ends 2.600 s, wait began 0.602 s, broke 2.602 s, class rung out of
step at once, port rung took at 2.757 s: the READ goes out again with its
whole 2 s, the next new command is refused by the operation's budget, and a
break of the READ finds the last rung whole inside 5.352 s.
usb-first-wait-spent, judged inusb_transport_break): a READwhose wait is unanswered for its whole 2 s (the staging now opens the call
from its own wait, not an earlier transfer's), a class reset out of step, a
port reset that takes:
a READ whose wait spent 2.002 s went out again after the port reset took, and returned the host's bytes.againforced false, the base's order):cargo test -p toyos-xhciEXIT=101 (the two new tests),--nightly usb_transport_breakEXIT=1,read refused=true matched=false.the call;
SilentReturnstill ends at 4.250 s.Defect 3: the end state, and the panic
What the stick last received. From the offline rung, a warm reset of port
13 with nothing after it (
reset=true and nothing sent after it, Reset Device=true); the READ it was owed data-in for was ended by that reset. Thenthe panic's reboot, 60 s later: every connected port reset, the controller
halted and reset, its ports unpowered,
no Bulk-Only command was open, so this reset cuts none. So every path this kernel drove ended with the device resetand inside no command, and the stick was still lost: its media had stopped
before the first READ, and nothing a host does on this machine clears that (the
T14's ports do not drop VBUS). Recorded in
issues/kernel/a-cut-bulk-only-command-does-not-brick-the-benchs-stick.md; nocode change.
The panic path quiesces USB already:
acpi::rebootcallsstop::before_reseton every reset, the panic's included, and run 84's pagecarries its account (
usb-quiesce: xHCI 00:14.0 5/5 connected port(s) reset … 16/16 port(s) unpowered). The panic atrootfs.rs:71stays: a boot with noroot is over.
Not stageable under QEMU either: its
usb-storagereturns to its resetstate on every port reset and every Address Device, so a second boot finding
the disk could not red. No staging was built.
Found and filed
issues/kernel/a-disk-operation-can-spin-past-the-tlb-ack-tripwire-before-its-break.md:CALL_AFTER_BREAKbounds the spin from the wait that broke, and an operation'searlier batches spin before it under the same lock: 1.99 + 4.75 s is past the
5 s tripwire. Arithmetic on the constants; not seen.
Run 86: the reset was judged before it finished (fixed in
0a6b7da5)The plain boot of the first round-11 head regressed on the T14: port 13's warm
reset was judged in the millisecond it was asked for, on
PORTSC 0x002212b1— PR still set, link RxDetect — because the PRC beside itwas a flag nothing had cleared, left by whatever reset the port before this
kernel ran. The scan called the link untrainable, skipped the port, found no
disk, and panicked in
rootfs.rs. The stick itself was fine afterwards.Portsc::reset_finishedis the completion rule — PR back to '0' and PRCor WRC set (xHCI 1.2 §4.19.5, §4.19.5.1) — and every wait for a reset asks
it: the boot scan's, the port machine's, the ladder's port rung's. The bound
is unchanged (
port::RESET_DEADLINE_NS, 2 s, the driver's transfer budget).port::reset_writeclears the reset-finished flags it saw, so the flag thatcomes up next is this reset's; the ladder's hand-written acknowledge beside
its own reset write is now that one write.
a_warm_reset_is_finished_only_once_the_controller_has_cleared_prwalks run 86's word and run 84's completion word on the same port.
reset_finishedback to the flag alone andreset_writewithout its acknowledge, the whole of this fix):cargo test -p toyos-xhciEXIT=101 on that test.--nightly xhci_EXIT=0with it applied, and that is the finding: QEMU never leaves a stale PRC and
clears PR with the completion in the same word, so no guest boot in this
suite can red on this. The reading is the specification's rule and run 86.
0a6b7da5; the table below is that run.Gates at
0a6b7da5(tree clean before and after)cargo test -p toyos-xhcicargo test --test toyos-build -- --nightly usb_transport_break… --nightly usb_reset_records_the_phase_it_cut… --nightly usb_12 passed, 1 quarantined and green: usb_disk_index_stable… --nightly xhci_… --nightly blackbox_cargo test(the fast tier)347 passed, 8 quarantined and green … 347 total (158.8s), 104 held back for the nightly tiercargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildcargo run -- --clippyEach arm — NOINHERIT, REISSUEOP (both measured at
d85d03df) and STALEPRC — wasapplied as a checked patch, run, reverted, and the tree read clean after it.
Unsure of
be enumerated untouched.
measured with this change; run 86 measured only a reset judged too early.
reset-finished flag, and clears PR with the completion in the same word.
T14
Two images of this head, requested:
usbbreak(theusual
usb-transport-breakarm) andjobcase(no staged break). Both mustshow
port 13 link already trained before this kernel ran; warm resetting itand then
port 13 enabled, speed=4beforeslot N enabled— which is what run86 did not do.
Round 10: the review of
ea1a44a6answeredCommits
02cf8114(the change; its title says "round 3" and means thisreview, round 2 of the review comments),
b0ea0007andee81dd4a(the twomoved-stick stagings boot four CPUs; the debt is named first).
origin/mainat
845f3e20(#461) is merged in as696a8633, no conflicts. Nothing abench line shows changed: the kernel's production change is the flush-debt
bookkeeping (the two-hop case, which no T14 run has reached: run 83 broke on
the first write, no flush owed) and
AFTER_BREAK's declaration moving, itsvalues unchanged; everything else is behind
boot-actuatorsparameters nometal row names. No T14 image is staged.
BLOCKER 1: the re-issue after a hold is timed from the break
usb-return-silentstalls the returning stick's bind 1.5 s (so it arriveslate in the held call, and still inside it) and leaves every transfer of the
operation the held call sends again on it unanswered, each wait spun to its
end (
msc::return_silent, checked inwait_transfer).Moved::SilentReturnrequires, off the kernel's stamps: the held call on the break's CPU is the
one that sent the operation again (
… went out again on it: it failed), thedevice went offline on the last rung, the call ended inside
AFTER_BREAK.whole()(4.75 s) of the break, and not before its waits couldreach where the last rung begins (4.25 s) — so the staging really spent what
the call had left. Head: 4.250–4.251 s in each of six runs (three at
b0ea0007, three atee81dd4aincluding both gate runs).Negative control REISSUEFRESH (the review's diff,
ctrl.after_break = if back { AfterBreak::CLOSED } else { call };, applied toee81dd4aand built):see the table. At
02cf8114it was redSilentReturn: the call its transport broke in ended 5.114 s after the break, past the 4.75 s.Why four CPUs. The held call sees the stick back only if a CPU in no call
on the held disk binds it. At two CPUs one REISSUEFRESH run put both in calls
on disk 0 until the call's bound (the held-disk issue's shape), so nothing
was sent again and the staging measured nothing; FlushedStick took that shape
in each of its six unmutated runs at two CPUs (the commit message of
ee81dd4asays five), and in REISSUEFRESH's alone run atb0ea0007the jobreset before the stick bound, which the judge's order refused. Both
now boot
smp: 4; the other moved-stick boots keep two, where the tolerantjudge already covers both shapes.
The judge reads the call that broke, not the first matching line. A call
on another CPU that found the disk held ends its own, and FlushedStick's first
run measured one such at 14 s from the break. The held call is now the first
… went out again on it/… is still held …line on the break's own CPU.BLOCKER 2: a carried debt is owed again
The debt is
toyos_xhci::flush::Debt, pure:wrote,flush(a carried debtfails the flush once, by name, without going to the device),
flushed,adopted(owed), andowed(no_cache) = carried || (unflushed && !no_cache).The kernel's
MscDeviceholds one;unflushedandflush_lostare gone.a_debt_carried_is_carried_again_until_a_flush_reports_itis the two-hopcase: write acknowledged on instance 1, adopted as 2 owing it, 2 leaves before
any flush and owes it, adopted as 3, whose next flush is
Lost, once.Negative control CARRYDROP (
owedwithoutcarried): red in that testand in
a_device_with_no_cache_owes_only_what_it_carries.BLOCKER 3: both edges of the debt
Pure:
a_write_is_owed_until_a_flush_that_succeeded_and_not_afteranda_write_on_a_carried_debt_outlives_the_flush_that_reports_the_loss. Staged:usb-transport-break-flushedbreaks the first WRITE(10) after a write wasreported complete and a SYNCHRONIZE CACHE then succeeded, with no write since
— kept in the actuator itself, not read off the driver's debt, so it stages
what it says whatever the debt holds.
Moved::FlushedStickrequires thatline before the break, the stick taken back as disk 0 with no
left owing a flushand noflush failed, and the job run to its reset. Atee81dd4athe held callsaw it bound 0.112–0.113 s after the break in each of three runs. Negative control NEVERCLEAR2 (
flushedleaves
unflushedset): red in both pure tests and inusb_transport_break;the FlushedStick judge now reads the debt lines before any other, so the red
names the debt.
NOTEs
AFTER_BREAKis declared once,toyos_xhci::call::AFTER_BREAK; the kernelaliases it and asserts
AFTER_BREAK.wait == USB_TIMEOUT_NS; the test'sbound is
call_after_break_secs(), read from it.kick_cpuis refused only beforeapic::init(main.rs), which runsbefore
smp::boot_aps: no other CPU exists to be halted then. Said inports_wanted's doc. The T14 boots x2APIC (run 83:LAPIC: x2APIC enabled (ID 0)).read_block_retrying's spin acrossBudgetExpiredcalls (up toblock::DEADMAN) is added to the held-disk issue; not fixed here.T14 run 79citations intests/common/usb.rsare gone.Gates at
ee81dd4a(tree clean before and after)cargo test -p toyos-xhcicargo test --test toyos-build -- --nightly usb_transport_breakb0ea0007… --nightly usb_reset_records_the_phase_it_cut… --nightly usb_12 passed, 1 quarantined and green: usb_disk_index_stable… --nightly xhci_… --nightly blackbox_cargo test(the fast tier)347 passed, 8 quarantined and green … 347 total (171.0s), 104 held back for the nightly tiercargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildcargo run -- --clippyMutations (each applied as a checked patch, built, the kernel rebuilt for the
kernel arms, and the tree restored):
ee81dd4a--nightly usb_transport_breakSilentReturn: the call its transport broke in ended 5.115 s after the break, past the 4.75 s a call may spin for(5.114 s at02cf8114, 5.112 s atb0ea0007)self.unflushed &= core::hint::black_box(true)inDebt::flushed)ee81dd4a--nightly usb_transport_breakFlushedStick: "… disk 0 came back on port 3 slot 1 as the same device (…); its volume carries on, and it left owing a flush …" of a stick that came back as itself02cf8114cargo test -p toyos-xhcia_write_is_owed_until_a_flush_that_succeeded_and_not_after,a_write_on_a_carried_debt_outlives_the_flush_that_reports_the_lossowedwithoutcarried)02cf8114cargo test -p toyos-xhcia_debt_carried_is_carried_again_until_a_flush_reports_it,a_device_with_no_cache_owes_only_what_it_carriesDebt::adopted(owed && false)in the bind)02cf8114--nightly usb_transport_breakOwedFlush: after the break, no line reads "usb-storage: disk 0 flush failed: …"The harness re-runs a red alone; REISSUEFRESH's alone run at
ee81dd4awas redon another boot:
test-runnerSEGFAULTexecute unmapped address at _start+0x0,[qemu] Init process crashed during boot— the unattributedshape Round 8 records under
log_flush_retry, n=1 here, in a mutated tree; nogate run at the head showed it.
Unsure of: SilentReturn and FlushedStick lean on four CPUs keeping one out
of every call on the held disk; a boot that puts three in calls at once would
miss the shape and say so by name, not pass. The 1.5 s stall leaves the held call
about 0.4 s of its 2 s operation budget to see the stick back: an estimate
from the constants (2 s, 1.5 s, the 0.1 s connect debounce), not measured.
Round 9: the review of
8dd2994cansweredCommits
d60cd335(the change),99a81a8c(late's test bounds its loop),f0695038(two defects the moved-stick boots then showed),9b577295(anissue file),
ea1a44a6(clippy).origin/mainhad not moved since909f34b4; nothing merged.Round 8's image booted as T14 run 82 (sha256
3b4a5bdc…84697): break at1.638 s,
disk 0 left port 13 … it is held1.756 s, the same serial bound onport 13 on cpu0 at 2.786 s,
is back … it completedon cpu4 at 2.803 s.BLOCKER 1: the whole call inside its bound, and the bind never on its CPU
AfterBreakis carried byservedacross everycommand of the operation, the hold and the command sent again, and closed
there; the bind closes each of its own commands;
scsino longer closes itper command. A later command of the same operation spends what the break
left it.
toyos_xhci::call::AfterBreak::hold): itopens the call where nothing broke, ends where the last rung begins, and the
command sent again is clipped there, as after a port rung that took, so a
break of it climbs on what is left. By construction:
every_path_through_a_call_ends_inside_its_boundswalks every sequence of 6steps (rung entered, rung taken, hold, spin to the window's end) from three
starts (a wait that broke at once, one that ran its 2 s, a call that found
its disk held) and asserts every path ends inside the bounds' sum;
a_command_sent_again_after_a_hold_leaves_the_last_rung_its_boundhas thelast rung whole after a hold. The kernel's
constassertion keeps that sum(4.75 s) under
tlb::ACK_TIMEOUT(5 s).try_lock(a bind canhold the lock for seconds), marks
PORT_WORK_ATdue and kicks every otherCPU once (the connect's interrupt may be its own CPU's, which takes none),
and takes the lock again for the re-issue only before the hold's end. Binding
runs where hotplug binds: the port machine, on a CPU that reaches a pass.
Moved::SlowStick,usb-slow-return: the returningstick's bind stalls 2.5 s): the break's call ends
is still held when this call may wait no longer … asked again2.000 s after the break on cpu0, thestalled bind is on cpu1, the disk is still taken back and the job runs to
its reset. Every moved boot that comes back asserts the held call ended
within 4.75 s of the break (from the kernel's stamps) and that a bind while
it held ran on another CPU. Six runs at
f0695038: SameStick 0.112–0.113 s,bound on cpu1; SlowStick 2.000–2.001 s.
Found while measuring, and fixed (
f0695038): with both vCPUs insidecalls on the held disk (a write and a read), no CPU took the pass, port 3 was
first enumerated at 2.455 s after both holds ended at 2.354 s, and the window
had passed: disk 0 lost. The window is the device's to arrive inside: a held
disk is not forgotten while a port reads connected and untaken, as it already
was not while an enumeration is under way. That shape still answers
BudgetExpiredto the calls that waited, which is filed:issues/kernel/a-held-disk-waits-for-a-pass-no-cpu-takes-when-every-cpu-is-in-a-call-on-it.md.And the host moved the stick on the HELD record, which reaches the console only
when
klogdruns; with the rung spinning, the cue could arrive after therung's 1.5 s (a device that left too late: offline). The staging writes its cue
to the console itself.
BLOCKER 2: a power cycle is not a move
MscDevice::unflushedis set by a WRITE the device reported complete andcleared by a SYNCHRONIZE CACHE that succeeded. A disk held while
owes_a_flush(unflushed, and notno_write_cache) is taken back with, and it left owing a flush of writes it had reported complete, so its next flush fails, and that flush fails once by name (flush failed: its device came back from leaving its port owing a flush …). How QEMU'susb-storagereports acache: it answers SYNCHRONIZE CACHE with GOOD (its
scsi-diskflushes thebacking), so this driver never latches
no_write_cachefor it; the driver doesnot read the caching mode page. Arms under QEMU:
Moved::SameStick(the breakis the boot's first write, nothing acknowledged: taken back clean, no failed
flush);
Moved::OwedFlush(usb-transport-break-owed: the first write thatgoes out while an earlier one is acknowledged and not flushed: taken back
owing, next flush fails by name). The T14 stick answers
does not implement SYNCHRONIZE CACHE, so it never owes one.BLOCKER 3:
Lateis puretoyos_xhci::late::Late, witha_ring_that_keeps_producing_is_given_up_on_one_ring_past_the_deadline(andthree more). The kernel wraps it with its clock and
RING_SIZE.BLOCKER 4:
usb_reset_records_the_phase_it_cutNot this branch's ladder, and not the driver:
main's5213b6e7("The loaderfiles the record's tail instead of scrolling it") moved the black box's ring
tail off the console into
loader.log. The wedge's own records(
usb-wedge: stopping every CPU at the,wedge: staged) are tail; theaccount naming the phase is head and was on the console in every red run
(
usb-quiesce: a Bulk-Only command was open in its data (unqueued) phase …).The QEMU half now boots a kept image and reads the wedge's records off
loader.log, asboot_deadline_ends_a_wedgedoes since that commit. Green atthis head, all four arms.
NOTEs
usb-serial-shortasks each disk for its serial string in8 bytes;
usb_transport_break's first boot requires every disk to sayserial number named and not read. The reviewer's mutation now reds there(
serial number "TOY.............").msc.rs:271:toyos_xhci::ladder::left(phase, data_out); acommand-phase break of a command with a Data-Out phase is owed data and enters
at the port rung. Test:
a_break_before_a_data_out_phase_is_done_leaves_the_device_owed_it.msc.rs:2307: after an operation that could not find its disk, or thatsaw it go held, a disk found here is back.
msc.rs:1991: a disk refused not ready (Bind::NotReady) or for want ofa pool block while a disk on its controller is held is enumerated again
(
AfterSlot::Again), not left refused; ports that read empty are servicedfirst, so a teardown frees its block before an enumeration elsewhere claims
one; and a held disk is not forgotten while its device's enumeration runs, so
its own bind (its whole
READY_BUDGET) never makes it late. The block arm isnot staged.
Address Device, SET_CONFIGURATION, TEST UNIT READY on the same slot) has no
hardware reading: runs 79–82 all read
nothing is connected. The stale-eventordering (the old slot's Disable Slot before the new Enable Slot) is not
stated in a test.
REMOVE
Deleted: the Round 7 sentence on pinning a CPU, Round 8's "not yet booted
there" and "Head image staged and requested", the "not this diff, #447's test"
clauses,
identity.rs's run-79 provenance, and theT14 run NNcitations inladder.rsandcall.rs.The two checks
Negative controls (
mut-usbtransport-9.sh: each anchor matched once, thediff non-empty, the kernel artifact newer than the mutation for guest arms, the
tree clean after; exits in
mut-exits-usbtransport-9.txtandwhole-exits-usbtransport-9.txt):kernel/src/drivers/xhciandtoyos-xhci/srcrestored to8dd2994c(0 diff lines in those paths butlate.rs, left unreferenced), tests kept, the three new actuators kept referencedserial number "TOYOS0BOOTSTICK1"read from a short answer. The per-blocker arms below reach the restLatenever gives up past the deadlinelate::tests::a_ring_that_keeps_producing_…every_path_through_a_call_ends_inside_its_bounds,a_command_sent_again_after_a_hold_…recheck_ports), as8dd2994cdidSameStick: the stick was bound on cpu0 while the call held for it spun there with IF clearOwedFlush: … no line reads ", and it left owing a flush …"tests/common/power.rsrestored to8dd2994c3 of 4 arm(s) unmet, the review's wordsladder::tests::a_break_before_a_data_out_phase_…serial number "TOY............."The pure arms ran at
99a81a8c(the pure crate is unchanged since); the guestarms at
f0695038.Independent oracle: QEMU's own xHCI and
usb-storagefor the move, theslow bind and a device that answers SYNCHRONIZE CACHE; the kernel's own stamps
for the bound; T14 run 82 for the move on hardware (the bind on another CPU
than the held call,
cpu0againstcpu4);main's5213b6e7for B4'spremise.
Gates at
9b577295Suites at
9b577295(tree clean before and after,gates-exits-usbtransport-9.txt);ea1a44a6changes one clone in the harness, after which clippy and
usb_transport_breakran again.cargo test -p toyos-xhcicargo test --test toyos-build -- --nightly usb_transport_breakea1a44a6; 6 of 6 atf0695038… --nightly usb_12 passed, 1 quarantined and green: usb_disk_index_stable;usb_reset_records_the_phase_it_cutamong the passed… --nightly xhci_… --nightly blackbox_cargo test(the whole fast tier)347 passed, 8 quarantined and green … 347 total (173.9s), 104 held back for the nightly tiercargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildtest result: ok, none failed)cargo run -- --clippy9b577295(redundant_clone,tests/common/usb.rs), 0 atea1a44a6Unsure of
call's commands that completed before the break are outside it, as on
main; so are the retries above the block layer, which spin withIFclearacross fresh budgets (
issues/kernel/syscall-preemption-is-incidental.md,issues/kernel/every-wait-in-this-kernel-is-a-spin.md).and goes offline behind the last reset.
in between; the window is judged at the next look, not at the connect.
usb_transport_break's second boot counted a boot-stick break(
no answer in the data phase in 2000 ms,0 TRB(s) unreached and no stop event) in 1 of 4 runs at99a81a8c; the same line is inmain-based logs ofother branches this session and in
issues/build/a-loaded-suite-reds-a-volume-checker-on-both-arms.md.Requested:
t14-request-usbtransport-9.txt, the head image once, sha256d777c6ae2d0c38e9b717d6628213c69301029b3977d0812d015e658a1d349903, armedusb-transport-breakalone.Round 8, head
da873d72a6f2a4df178e04c23cd6d7d463de9dac: a wait reads the ring before it believes its deadlineorigin/mainat0773ae87(#476, #477) merged in ascd0a6b1a, no conflicts.Then
a0926caa(the fix) andda873d72(an issue file for an unrelated red).The cause
PR CI run 35661087102 (head
98f58bdf) redded four shards: boots whose programsdied at
_start(execute unmapped address, PML4E 0) and host-sideother errors. The one capture carrying the whole console (guest 10,screen_console_panic):The Stop Endpoint's wait returned 425 ms past the class rung's 750 ms window,
which a loop reading the clock every iteration cannot do while its CPU runs; and
the endpoint read
Stoppedright after, so the command had been answered. QEMUruns an xHCI command inside the vCPU's doorbell write, and a Stop Endpoint on a
Bulk-In whose packet belongs to an in-flight SCSI request cancels it
synchronously (
scsi_req_cancel→blk_aio_cancel, recalled from QEMU's sourceand measured below), waiting out the host's flush. The runner's first flush took
~3.2 s by the two waits' sum: 2 s broke the SYNCHRONIZE CACHE's status wait, the
rest held the vCPU in the doorbell write.
wait_commandchecked its deadlinebefore the ring, so it returned "unanswered" with the answer in the ring; the
call then refused every command and the boot stick went offline. The ordering is
main's too; what made it fatal here is round 6's per-rung bound (class reset0.75 s), where the old single 4 s budget left the stall room.
Reproduced on this host under TCG by delaying the boot stick's first flush
3.2 s (QEMU
blkdebuginject-errorwithevent=none,iotype=flush,errno=0, once=on,delay-ns=3200000000on the harness's-drive, an uncommitted patch):the head reds with CI's lines (
Stop Endpoint timed out after 1202 ms,class reset was not answered, offline,SEGFAULT … _start+0x0).The fix (
kernel/src/drivers/xhci/wait/mod.rs,Late)wait_commandandwait_transferread the ring before they believe thedeadline; past it they still read, giving up at the first empty read or after
one ring's worth (
RING_SIZE), so a ring that keeps producing still cannot holdthe bound open. With it, the same staged flush reads: the Stop Endpoint
answered; the class rung's next command
not issued: the bound on this part of the call is spent(the stall ate the rung);the port reset took … TEST UNIT READY under its own tag; the SYNCHRONIZE CACHE refused as ask-again on itsoperation budget;
SCSI 0x28 completed after 2 break(s) running; the transport came back.main(0773ae87, the whole change reverted in this worktree underthe same flush) breaks the same way and survives on the full 2 s Stop Endpoint
wait (
endpoint 3 … recovering2.369 s, next line 3.574 s).The T14. The change touches the recovery path's waits and no rung, bound or
step. On hardware a CPU is not held inside a register write, so a wait's
deadline passes with the ring empty exactly as before: no bench line of runs
80/81 changes.
The two checks
Negative control (24 names CI redded, each alone, one filter per run, under
the staged 3.2 s flush;
names-*-usbtransport-8.txt): the fix reverted (thehead before it) 24 of 24 red; with the fix 24 of 24 green;
main2 of 2green. The fix is one file, so reverting it is the whole change.
Independent oracle: QEMU's own xHCI and
usb-storageagainst a host flushdelayed by QEMU's
blkdebug, and CI run 35661087102's record, whose lines thestaged arm reproduces one for one.
Gates at
da873d72, tree clean (gates-exits-usbtransport-8.txt)cargo test(the whole fast tier)345 passed, 8 quarantined and green … 345 total (153.6s), 103 held back for the nightly tier. The run before it ona0926caaexited 1 on90_stdio_bufferingalone (a 10000-byte line captured as 7168 + 2832; green alone); filed asissues/build/stdio-buffering-saw-one-long-line-arrive-as-two.md, base not measuredcargo test --test toyos-build -- --nightly usb_usb_reset_records_the_phase_it_cutalone,3 of 4 arm(s) unmet; on the tree before the fix (cd0a6b1a) 1, the same line. Every otherusb_*passed,usb_transport_breakamong them… --nightly xhci_… --nightly blackbox_cargo test -p toyos-build --libcargo test -p toyos-xhcicargo test --workspace --exclude toyos-buildtest result: ok)cargo clippy -p toyos-xhci --all-targets -- -D warningscargo run -- --clippywall_clock_rtc_unstablealone oncd0a6b1a, before the fix and without the staged flushUnsure of
maintoo): 2 s isthe status wait's bound for every command, and a hosted runner's first flush
exceeds it. Now it costs a port reset, not the disk. Filed already as the
screen_fatal_haltquarantine's issue.as a rung that did not verify, which is true of the call and not of the device.
Round 7, head
c819b02cf9760022a8ea214640955c264c637ee6: the same device, come back, is the same diskFour commits after the merge:
3c8785b0(the change),59099fd7(the moved-stickjudge takes run 74's shape too),
dcf5a4bd(the issue file),c819b02c(clippy).
What the bench read (run 79, round 6's image
77fbcc71…)Break at 1.842 s, entered at the port rung (correct per round 6).
port 1 reset while recovering (hot on a USB2 port) … reset, and the port is enabledat1.897 s;
Address Device (after the port reset) failed: code 4at 1.948 s — thestick had left the USB2 half; the last rung found port 1 in RxDetect.
write of 1 blocks at 11399 failed on disk 0. At 2.876 sport 13 connected, link already trained, speed 4, the same0781:5581, anddisk 1 ready on slot 6, 7507812 blocksat 2.878 s — 0.98 s after the reset. The boot parked to its deadline andread WEDGED because disk 0, the root volume, was offline. No replug; the stick
enumerated at once under Ubuntu and the log partition checked clean.
What changed
Identity (
toyos-xhci/src/identity.rs, pure, 6 tests).same(left, arrived)requires every field to agree: the device descriptor's idVendor,idProduct and bcdDevice (USB 2.0 §9.6.1), the string its iSerialNumber names
(§9.6.1, §9.6.7), INQUIRY bytes 8–36 — vendor, product, revision — and READ
CAPACITY's sector count and size. Speed, port, slot and endpoint descriptors
are not identity: the move changes all of them. The bind reads the serial
(string descriptor 0 for the first LANGID, then iSerialNumber, into the disk's
own data buffer) and logs it:
usb-storage: slot N serial number "…".Why a different stick plugged in during the window cannot be adopted.
Every field but the serial number is the model's: a second SanDisk Ultra of the
same size answers the same USB ids, bcdDevice, INQUIRY and capacity. The serial
number is the one field a unit carries of its own — the Bulk-Only class
specification requires a mass-storage device to publish a unique one (BOT 1.0
§4.1.1, recalled, not read this round). So a device whose serial is absent or
unreadable is never adopted, as the disk that left or as the device that
arrived (
Differs::LeftUnnamed/ArrivedUnnamed): identity cannot beproven there, and it is not assumed. What this cannot tell apart is two units
sold with one serial number, which some cheap sticks are; the capacity and
INQUIRY checks then still bind it to the model, and no more.
The companion port, and why the driver does not need it. xHCI 1.2
§4.19.7: "The mapping of xHCI Root Hub Ports to the physical USB connectors of
a system is defined by platform implementations and outside the scope of this
specification." The Supported Protocol capability (§7.2) says which port
numbers speak USB2 and which USB3 (on the T14 port 1 is USB2 and 13 USB3) and
never which two share a connector; Appendix D gives that to ACPI
_UPC/_PLDgroup tokens, which this kernel does not parse. Read off
scratchpad/r2-xhci-spec.txtthis round. A companion link could only saywhere to look — and the port machine already enumerates whatever connects
anywhere — and could never say the device is the same one. Identity answers
both, so no pairing was built and the port is not checked.
The bound.
RETURN_WINDOW = 2 sfrom the reset this driver made:twice run 79's 0.98 s (reset to bound on port 13), and a
constassertionholds it at no more than the port rung's and last rung's bounds together (1.5 +
0.5 s) — what the ladder already gave a reset device to answer, which a device
that left has made moot.
identity::returns_by(reset_at, now): a disk is heldonly if this driver reset its port and it is seen gone inside the window; a
device that leaves with no reset of ours was unplugged, as before.
Where the wait is, and why it cannot starve a rung. A disk is held at one
of two points: its port read empty inside a call (the port rung's reset found
nothing connected, the last rung's did, or a transfer's wait saw the port go),
or its port's teardown ran within the window. It leaves its pool block (which
stays claimed by the old port until that teardown) for a per-controller list;
the poll forgets it when the window passes, saying
disk N did not come back within 2000 ms of its port reset; it is offline, and wakes for that. Anoperation on a held disk (
storage_read/write/flush→served) waits afterthe ladder has ended — the last rung has run, or found its port empty — so it
spends nothing any rung is owed. It waits with the controller lock given back,
stepping the ports itself (
recheck_ports, 1 ms apart), because its CPU isthe one it holds; the enumeration and bind of whatever arrives happen inside the
wait. It ends one of three ways: the disk is back (the operation is issued again,
whole); the window passed (
Device, as any offline disk's operation); or thecaller's own
block::OPERATIONpassed (BudgetExpired— nothing was issuedwhile it waited, and every caller asks again above its locks).
What a filesystem sees; a write in flight when the device vanished. A write
reports complete only on its own CSW with status 0. A device that left inside
a write sent none, so that write was never reported complete; the reset that
moved it ends the command at the device (USB 2.0 §9.1.1 — every state returns
to Default). The operation is issued again whole, from the caller's own
buffer: every CDB here is idempotent, so blocks the device took before it
left are written again with the same bytes, and a block it took part of is
written whole. The re-issued command is the one that broke: the operation is
serialized by the disk's block-layer lock, so the call that broke is the one
that waits and re-issues (
disk 0 is back, and the operation it was asked went out again on it: it completed). If the window passes first the write fails astoday (
Device) and the medium at that block is whatever the stick committed —as today. The old slot's rings are torn down (Disable Slot) before its pool
block is reused, so nothing of the old command can land later.
A new disk, not the old one. A device that differs is refused by name (
the device on port P is not disk N come back: <field>) and bound under a newnumber; the old disk stays held and is lost when its window ends.
Tests
usb_transport_breakboots seven times now; two are new.Moved::SameStick): the jobcase boot off the bootstick with
usb-transport-breakand a new actuator,usb-reset-moves, whichholds the port rung's first reset (within the rung's bound) until the port
reads empty. QEMU cannot move a device on a reset, so the host does it:
device_delof the stick, thenblockdev-addof the same backing file(
locking: off— the unplugged drive may still hold it) anddevice_addonport 3 with the same
serial=. Judged in order: the break, the entry at theport rung, the hold,
disk 0 left port 1 (…) after this driver reset it; it is held,port 3 connected, the same serial read,disk 0 came back on port 3 … its volume carries on,disk 0 is back, and the operation it was asked went out again on it: it completed, and the job'sRebooting.;Boot: complete;nothing
failed on disk 0, no budget refusal on disk 0, nodid not come back, nodisk 1 ready; a clean console. The host's move can land after therung's bound (measured once in four boots): the reset then verifies, the
stick leaves at the next look and is held by its port's teardown — run 74's
shape — and the judge accepts it and names which shape it saw; only the first
shape proves the write that broke is the one re-issued.
Moved::AnotherStick): the samebacking under another serial number — everything a second unit of the model
shares with the first. Judged in order: held,
the device on port 3 is not disk 0 come back: its serial number differs,disk 1 ready on slot,disk 0 did not come back within 2000 ms of its port reset; it is offline; neverdisk 0 came backnordisk 0 is back.serial=TOYOS0BOOTSTICK1: QEMU's default serialis built from the port path, so the same stick on another port would read as
another unit.
request before it being a GET_DESCRIPTOR; the bind's (and OVMF's) string
reads broke that. It now asks whether a GET_DESCRIPTOR(Configuration) came
since the last SET_CONFIGURATION (
is_a_rungs_configuration).transport_break_on_metal) accepts the moved shape: held,as the same device,went out again … it completed, nodid not come back, then the DONE chain.The two checks (device driver; a device's identity across a trust boundary)
Negative controls (
mut-whole-usbtransport-7.sh, exits inmut-exits-usbtransport-7.txt; each under a restoring trap, tree clean after):kernel/src/drivers/xhci/{device,mod,wait/boot,wait/msc}.rsandtoyos-xhci/src/lib.rsrestored to the merge base7f4155b1,identity.rsdeleted (635 lines out); only the staging kept (theusb-reset-movesactuator and its 19-line hold) and every testafter the break, no line reads "usb-storage: disk 0 left port 1 (its port read empty) after this driver reset it; it is held "— the first four boots pass, the moved boot reds; the head's kernel prints that line, so the arm's was the one bootedsame()ignores the serial numberidentity::tests::a_device_that_differs_in_any_field_is_a_new_disk; the negative control boot:disk 0 came back on port 3 … serial number "TOYOS0OTHERSTICK" … its volume carries on, and nois not disk 0 come back: its serial number differsIndependent oracle: the specifications (xHCI 1.2 §4.19.7, §7.2 and Appendix
D, read; USB 2.0 §9.1.1, §9.6.1, §9.6.7 and BOT 1.0 §4.1.1, recalled; SPC-4
§6.4.2 for INQUIRY's fields, recalled); run 79's record, which is the failure
this round is built from and whose numbers (1.897 s, 1.948 s, 2.878 s) are a
pure test; QEMU's own
usb-storageas the device; and the requested T14 boot.Gates at
c819b02c/59099fd7(gates-exits-usbtransport-7.txt)The guest suites ran at
59099fd7;dcf5a4bdis an issue file andc819b02ctwo clippy attributes in the pure crate, no behaviour.
cargo test --test toyos-build usb_ -- --nightly(12 + 1 quarantined)usb_reset_records_the_phase_it_cutalone,3 of 4 arm(s) unmet; every otherusb_*passed,usb_transport_breakamong them (26 s);usb_disk_index_stablequarantined and green… xhci_ -- --nightly(16)… blackbox_ -- --nightly(7)usb_reset_records_the_phase_it_cuton the base7f4155b1(tree restored to it, 0 diff lines)3 of 4 arm(s) unmetlog_flush_retryhead / base7f4155b1the retried flushes left the log volume breaking the format) — one run each, not a rate; tracked inissues/boot-media/log-flush-retry-reds-two-ways-at-two-in-five.mdcargo test -p toyos-xhcicargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildtest result: ok)cargo run -- --clippy(the five shapes ofsrc/clippy.rs)c819b02c(1 atdcf5a4bd:large_enum_variantandtype_complexityinidentity.rs, fixed byc819b02c)Unsure of
halves. SanDisk sticks publish one and Linux shows one serial for the device;
that the USB2 and USB3 enumerations give the same string is expected, not
measured. If not, the requested boot reds by name (
is not disk 0 come back: its serial number differs) and ends as run 79 did.own TEST UNIT READY may take
READY_BUDGET(0.5 s) on a stick spinning up.comes back after the write's 2 s
block::OPERATION, the write answersask-again, and
logdgives up its volume on an append that is refused(filed:
issues/boot-media/logd-ends-the-boots-log-on-one-refused-create-and-nothing-durable-says-so.md).MSC_BLOCKSis 2 per controller: the old port's block is freed only at itsteardown, so on a controller already serving two disks the returning device
could be refused for want of a block until that teardown. Not staged.
comes back at 2.1 s is a new disk, as before this round.
(
issues/hardware/the-t14s-superspeed-stick-enumerates-at-high-speed-under-toyos.md),the FAT long-name orphan, logd's give-up — all filed.
issues/kernel/a-disk-taken-offline-is-never-brought-back.mdnow says themoved half is held by identity, QEMU-judged and not yet T14-judged; its second
half (offline for the connection's life) stands.
Requested (
t14-request-usbtransport-7.txt): the head image once, sha2561e9e6a0c714ce2103419d67db2857f843c9c12f42e5d86f8b9605543a60e486b, armedusb-transport-breakalone.Everything below is round 6 and earlier as they were written.
Round 6, head
acefbc1592c32510b012921829f6789f7ac0f6d5: a ladder with a bound per rung, and a device owed data is reset at its port firstOne commit for the change,
b684a622, and two after it:7b312d6e(the port rung's 50 ms and the offline line's wording),acefbc15(the whole-change control's needle).What the bench read (run 77, read out by run 78, 2026-09-21)
Round 5's image, stick on slot 1 / port 1 (the USB2 half, speed 3):
No
is a status behindline and noReset Recovery took. Linux afterwards:usb 3-1: device descriptor read/64, error -71,device not accepting address, until a replug.A. What the stick does at a class reset, settled against the records and the texts
Refuted: round 5's account, that the stick keeps the abandoned WRITE's
status and hands it to the next reader. Run 77 read the Bulk-In after the
class reset for 2000 ms and nothing came.
Supported: the stick answers the Bulk-Only reset and both clears and does not
leave the WRITE it was given. BOT 1.0 §3.1 has a device that answers the
reset "ready … for the next CBW", and §5.1 has a CSW echo its own CBW's tag.
Runs 74 and 75 (round 4): the first status after the class reset carried
0x58a, the abandoned WRITE's tag, not the re-issued WRITE's 0x58b. Run 77: the
TEST UNIT READY's 31 bytes were taken (
endpoint 4 with 0 TRB(s) unreached)and no status came for it (
endpoint 3 … 13 B unmoved); the second one's 31bytes were not taken in 2000 ms. A device that had left the WRITE would have
answered 0x58b, and would have answered a TEST UNIT READY.
Refuted: that it takes whatever arrives next on the Bulk-Out as that WRITE's
data, byte for byte, and so that round 4's retry wrote a block whose first 31
bytes are a CBW. The saved volumes say otherwise.
t14-run76/sda3-before-flash.imgis the stick after run 75: sector 1080 of the log partition (the write in
question,
11399 × 8 = 90112 + 1080) beginsTOYOS-LOGat offset 0 with thenew long-name entry at 0x60, and
grep -a -b -o USBCover the partition findsnothing. The re-issued WRITE's 4096 bytes are on the medium, aligned, under the
abandoned tag.
t14-run78/sda3-before-flash.imgis the stick after run 77: noUSBC, and sector 1080 without the new entry — the TEST UNIT READY's 31 byteswere not committed either. What the stick did with those command blocks — took
them as command blocks while still owing the first WRITE's status, or discarded
them — no record says.
What no path of this driver can now do is put bytes on the Bulk-Out of a
device in that state: a break that leaves a device owed Data-Out bytes enters
the ladder at the port reset, and the first thing on its Bulk-Out afterwards is
a TEST UNIT READY that follows a reset and a SET_CONFIGURATION (judged on
QEMU's wire capture, below). A break anywhere else still gets the class reset
and its TEST UNIT READY: 31 bytes,
dCBWDataTransferLength0. At a device thathas wrongly stayed in a Data-Out phase that is less than one logical block of
any size, the next rung resets the port before anything else is sent, and both
saved volumes show this stick committing none of a command block.
Why the port reset first, and not the class reset. The bench has the other
half of the experiment already:
issues/kernel/a-cut-bulk-only-command-does-not-brick-the-benchs-stick.mdrecords six boots (runs 39, 43 ×3, 45, 47) in which this stick was left holding
a CBW with no data, a queued data phase, an unread status, or a write in
flight, and had its port reset with no class reset before it: it enumerated
0 s after the machine came back, every time. The three boots of this branch
that cost a replug (66, 75, 77) all ran the class reset at a device inside a
command first; the one that did not (74) is the one whose WRITE the stick had
been let finish. That is six to three, on one stick, and it is what Linux's
usb_stor_invoke_transportdoes too: port reset first, the class reset only ifthat fails (recalled, not read this round).
Run 77's bus reset did happen, and did not bring the stick back.
PORTSC … then 0x00000f91has PR set: round 5 wrote the reset with nothing left to waiton, so nobody saw it finish, acknowledged it, or addressed the device. Unlike
run 74 no
port 1 disconnectedfollowed. So fact 4 stands only for a stickthat had been let finish its WRITE; for one that had been given a class reset
and two command blocks inside it, a bus reset was not enough. Whether it is
enough with no class reset before it is what the requested boot asks, and the
six boots above are why it is asked that way.
Texts: BOT 1.0 §3.1, §5.1, §5.3.3–5.3.4, §6.6.1, §6.7.3 and USB 2.0 §7.1.7.5,
§9.1.1, §9.2.6.2, §9.4.5, §9.4.7 are recalled; no copy is on this machine.
xHCI 1.2 §4.3.1 (PRC is the reset's completion), §4.6.5 (Address Device with
BSR clear is defined from Default), §4.6.8–4.6.11, §4.19.5, §5.4.8 (PR, WPR)
were read off
scratchpad/r2-xhci-spec.txtthis round.B. The ladder (
toyos-xhci/src/ladder.rs, pure;msc.rstakes the steps)clears, ended by TEST UNIT READY under its own tag. Anything else — another
tag, a stall, silence, a request not answered — is the next break.
for on PRC (§4.3.1), its flags consumed as an enumeration's are; 50 ms
(USB 2.0's 10 ms reset recovery, and the 40 more Linux gives); Reset Device (§4.6.11: slot to Default, address
0, every endpoint but EP0 Disabled); Address Device with BSR clear (§4.6.5),
whose SET_ADDRESS is the first thing the reset device answers;
SET_CONFIGURATION with the bind's value; Configure Endpoint adding the pair
as the bind described it; TEST UNIT READY. On the slot, the pool block and
the disk number the device already has, so the caller's command is
re-issued and a mount carries on: under QEMU the boot stick's own staged
break comes back this way with the root volume on it. The rung's input
contexts are a page of the disk's own, not the controller's shared one,
which an enumeration outstanding on another port may still be read from.
waited for, whichever step the device stopped answering at, Reset Device,
the slot back from the poll. Nothing is sent after the reset. A device that
left its port is its port's teardown's, as a round trip whose port read gone
already was.
A rung is climbed once per run of breaks: a rung that verified and whose
command broke again is not asked again, the next break climbs on, and only a
completed round trip ends the run.
MAX_TRANSPORT_BREAKSis the number ofrungs.
The bounds (
toyos-xhci/src/call.rs). Fact 3's defect was one budget foreverything after the break. There is now one per part — the wait that broke and
re-issued commands 2 s, class reset 0.75 s, port reset 1.5 s, offline 0.5 s —
and every wait is clipped to where the rungs still ahead of it begin, so a
rung is entered with the whole of its own bound whatever was spent before it,
and the call still ends at their sum, 4.75 s, asserted under the 5 s TLB-ack
tripwire the caller's
IF-clear spin answers to. A controller that left acommand unanswered is sent no other in the call, in any rung, and its port is
still reset: that is a register write.
Gone with round 5's account:
bot::whoseand the reading-on past an abandonedstatus; and
PortState::taken_offline/AfterSlot::Revive, the port'senumerate-again-once after offline, which the port rung now does in place and
with the disk kept.
C. The end state, whichever rung ends the climb
the port reset tookusb-transport-offline; the faults gate's third break; a bind's;usb-reset-breakport N reset=true, the last thing it was sentnothing is connected, thenport 13 connectedWhat QEMU cannot show is the thing the ruling is about: that this stick's
firmware is left enumerable. Its
usb-storagecomes back from any reset. Thebench's
the boot stick enumerated N s after the machine answeredis thejudge, and the requested boot reads it.
Tests
usb_transport_breakboots five times.data and enters at the port reset; on the wire (QEMU's pcap) no Bulk-Only
reset and no command block stands between the abandoned WRITE's CBW and the
rung's SET_CONFIGURATION, and the first block after it is a TEST UNIT READY;
every block verifies host-side.
then the port reset (4 Bulk-Only resets each followed by both clears and a
TEST UNIT READY; 4 rung SET_CONFIGURATIONs each followed by one), one class
reset staged out of step, the fourth run offline behind its last reset with
the slot back while still plugged in; then a disk whose INQUIRY breaks the
budget over inside its bind climbs the same ladder.
usb-transport-offline, which now stages run 77's starvation: everyrung's TEST UNIT READY is unsent and unanswered for the rung's whole bound.
The port rung really spends 1.5 s (read off the kernel's own timestamps), and
the last rung must still stop both endpoints, see its reset complete, tell
the controller and give the slot back, with no
not issuedline.and the sealed section carries
the port reset took.log_flush_retry's hung device (usb-reset-break, now a whole climb thatanswers nothing) goes offline through the port rung.
The two checks (device driver; concurrency with the event ring)
Negative controls,
mut-usbtransport-6.shon the clean head: exact-oncereplacements under a restoring trap, each tree shown to build, exits in
mut-exits-usbtransport-6.txt.kernel/src/drivers/xhci,usb_gate.rs,actuator.rsandtoyos-xhcirestored to37d30495(0 diff lines against it, and 0 against round 5's driver907fbe37: the driver run 77 measured); this round's tests keptthe driver never said "… is owed the data of the command that broke, so nothing can be asked of it on the Bulk-Out: its port is reset with no class reset before it"(mut2-exits)ladder::tests::a_device_owed_data_out_is_never_given_the_class_reset; the same line as WHOLE (mut2-exits)refused=true matched=false" was not answered; break ": a rung refused without a device refusing ittransport broke on the port reset's TEST UNIT READY: … break 2 of 3underusb-transport-offlinethe last rung's reset was not seen to completecall's tests;is offline: … Stopped=false, port 2 reset=true …, Reset Device=false, its slot is kept until the unplug— run 77's shape — and the test wantsStopped=trueIndependent oracle: the texts above; QEMU's own capture for the wire; the
saved volumes of runs 75 and 77 for what the stick committed; and the bench
records of runs 39–47 and 74–78, which this round is built from and the
requested boot judges it against.
Gates at
acefbc1592c32510b012921829f6789f7ac0f6d5, tree clean before and after (gates-exits-b-usbtransport-6.txt)cargo test -p toyos-xhci(90 tests);cargo testintoyos-xhci/simcargo test -p toyos-blackboxcargo test -p toyos-build --libcargo test --workspace --exclude toyos-buildcargo clippyshapes ofsrc/clippy.rsusb_transport_break,blackbox_done_chain,blackbox_panic_chain,boot_deadline_ends_a_wedge,hard_lockup_ends_a_deaf_cpu,usb_reset_hands_devices_back,blackbox_foreign_record,blackbox_early_panic_sealed,blackbox_fault_sealed,machine_reboot,usb_storage_gate,usb_storage_shapes,usb_short_read,usb_boot_stick_pulled,xhci_hotplug,usb_disk_index_stable,usb_refused_disk_first,usb_storage_write_error,usb_flush_optional,usb_pool_exhausted,xhci_hid_break,xhci_flap, each-- --nightlyusb_reset_records_the_phase_it_cut3 of 4 arm(s) unmetas on the base37d30495this session (ab-base-exits-usbtransport-6.txt: base 1) and as rounds 4 and 5 measured; #447's test, not on--known-red's listlog_flush_retry37d30495this session 3 red of 3 (ab-base-exits), on the format checker and the deadman, in its first two boots, which arm no transport break. Its third boot (the hung device) reaches this round's port rung and goes offline through it in the one run that got there (guest-a-log_flush_retry:the port reset was not answered; break 2 of 3,is offline: … reset=true …). Tracked:issues/boot-media/log-flush-retry-reds-two-ways-at-two-in-five.mdThe gates ran twice, at
b684a622(gates-exits-a) and at7b312d6e(gates-exits-b), the same exits both times;acefbc15changes one test's needle and was judged by the WHOLE and ENTRY arms above and the test build.Chosen between, and unsure of
nine boots of one stick, not a specification. BOT §5.3.4 makes Reset
Recovery the host's answer to every error, and a compliant device honours it
anywhere. The texts permit a port reset at any time, so it costs a compliant
device an enumeration and nothing else.
Then the rung reads the port empty, the disk is its teardown's, and the stick
arrives under a new number: alive and enumerable, which is the ruling, and
with the root volume lost, which is
issues/kernel/a-disk-taken-offline-is-never-brought-back.md.measured. Run 75's was hot, after a class reset.
the port: §4.19.5 asks for one or the other "immediately",
portsc::Writeoffers no PED by design, and nothing measured says which the stick prefers.
the bench timed (run 74).
issues/hardware/the-t14s-superspeed-stick-enumerates-at-high-speed-under-toyos.md(fact 5, with the readings, not chased).
.claude/agents/implementer.mdis not onmain; read from the job scratchpad.Requested then (
t14-request-usbtransport-6.txt, booted as run 79): the head image once, sha25677fbcc71fcce8526a47c76f9d1477986bb5f7d672193e7555019d8052082a25a.Everything below is rounds 1 to 4 as they were written. Where it says a
recovery whose every step answered says
Reset Recovery took, that a break isalways answered with the class reset, that the give-up writes
PORTSC.PRonone budget with the recovery, and that a disk given up on is left with its port
believing something is attached, round 6 above is what the branch now does.
What the record says
Run 55's page, as the next boot's loader printed it (the page had dropped its
227 oldest records, the first break among them):
22
transport brokelines in the 85 ms from 3.471 s to 3.556 s, then a commandthe controller never answered. Code 3 is Babble Detected Error and 4 is USB
Transaction Error (xHCI 1.2 Table 6-90); endpoint 3 is the Bulk-In, 4 the
Bulk-Out. Run 24 shows how one starts:
no answer in the status phase in 2000 mswith both endpoints Running — a CSW two seconds late, abandoned.Why the two codes alternate is not established. The first round's body said
each recovery reset one pipe at both ends and the other at the host only. That
is not what the base did: the pipe that was not halted got Stop Endpoint and
Set TR Dequeue and no ClearFeature, and neither command zeroes a toggle or a
sequence number (§4.6.9, §4.6.10), so both ends of that pipe were left
agreeing. Nothing in either record reads a sequence number or the device's
state. What the record does establish is below.
Where the base was wrong, against the texts
Bulk-Only Mass Storage Reset, then ClearFeature(ENDPOINT_HALT) on the
Bulk-In, then on the Bulk-Out — not conditional on a halt. The base sent a
clear only for the pipe its controller reported Halted. And a clear cannot
be sent alone: it reinitialises the device's data toggle whether the endpoint
was halted or not (USB 2.0 §9.4.5), so the host's own has to be zeroed to
match — Reset Endpoint does that from Halted (xHCI 1.2 §4.6.8), and a
Configure Endpoint with Drop and Add set does it from Stopped (§4.6.8's
note).
Three breaks returned
Scsi::Broken→BlockError::Device, whichfile_backing::read_block_retryingreturns at once (it asks again onBudgetExpiredalone). The repeats in the record —root: read of block 3 failedat 3.502, 3.517, 3.525 and 3.540 s — are new callers, each given three fresh attempts at adisk nothing had marked failed.
command, one endpoint halted. Nothing past §5.3.4 was taken; the bus reset
came with the platform reset at 252 s, and by the first round's account of
runs 55 and 56 the stick then needed a physical replug before it answered.
The change
The recovery (
toyos-xhci/src/reset_recovery.rs, pure;msc.rstakes thesteps). Both endpoints to Stopped — Reset Endpoint from Halted, Stop Endpoint
from Running, Set TR Dequeue Pointer from Error (§4.6.10), nothing from
Stopped — then one Configure Endpoint dropping and adding both on fresh rings,
then the class's three requests.
An event outranks the Endpoint State field. §4.8.3's note has the field's
update deferred past an error and past a command's completion, and tells
software to keep its own image from events.
Quiescingis that image for onequiesce: the transfer event that ended the round trip is where a pipe starts
(Stall, Babble, USB Transaction and Split Transaction Error are Halted, TRB
Error is Error —
Broke::Codecarries its pipe soscsican hand it over); acommand that succeeded leaves its pipe Stopped whatever the field goes on
reading; and a Context State Error rules out, for that pipe, the state the
command was chosen from. A field that moved is believed; one that still reads
the ruled-out state is taken to lag, and the plan goes on to Halted and then
Error, the two states §4.6.9's note says a Running endpoint reaches by itself.
Round 2 re-read the same field with no delay: on a controller that defers the
write it spent its five looks in microseconds and took a healthy disk offline.
Seven looks at most now — three commands per pipe and the look that finds both
Stopped — asserted to be the worst case and not a margin, over all sixteen
pairs, every move under the look, with a field that is current and one that is
never written again. What a Context State Error means, what any other code
means and what silence means are the crate's decisions; the driver's loop
issues what it is told and ends where the plan ends.
The input context is the pure crate's (
toyos-xhci/src/configure.rs). Everydword the Configure Endpoint reads — Drop flags, Add flags, slot context, both
endpoint contexts — is decided by
configure::bulk_pairand handed to thedriver as a list it writes without looking inside, so the driver has no flag
left to omit. The recovery's slot context is the specification's: §6.2.2.2
asks for Context Entries and the Hub fields, and Table 6-7 has USB Device
Address and Slot State initialised to 0 as input. Round 2 copied all four
dwords out of the output context, both of those non-zero, and cited §6.2.2.2
for it; the copy and the sentence are deleted.
One budget for the whole call (
toyos-xhci/src/call.rs,CALL_AFTER_BREAK). Round 2 sent a controller that had left one commandunanswered another from
take_offlineand then waited on a port reset: up tofour 2 s spins in one call, under
XHCIwith preemption off, against a TLB-acktripwire of 5 s. The budget opens at the start of the bulk wait that broke and
is 4 s, twice the driver's one timeout. Every later wait of the call is clipped
to it; a step reached with nothing left is not taken and says so; a command
that got no answer ends further commands to that controller in that call, by
name; a command is not re-issued into a spent budget. A
constassertion holdsthe budget under
tlb::ACK_TIMEOUT. Measured by hand onaf1dd433plus anuncommitted probe patch (one gate read with the bulk endpoint's doorbell and
the command ring's both withheld for the whole call; TCG, dev host): read began
0.200 s,
break 1 of 3at 2.200 s,Stop Endpoint timed out after 1999 msat4.200 s,
Stop Endpoint not issued: the controller left an earlier command of this call unanswered, offline withStopped=false … its slot is kept until the unplug, read returned 4.201 s — 4.001 s, and 4.002 s on the harness'sre-run. The probe's first run, one commit earlier, panicked the kernel on a
wrapped subtraction in the settle;
af1dd433fixes it and moves the arithmeticinto the crate with its test.
Round 2's hand probe, quoted in
2527bfff's message, ran on the uncommittedtree before that commit (a 96-line dirty diff in
wait/mod.rs), which thatmessage does not say and history is not rewritten to add.
Take-offline is one path, inside a bind too. Both endpoints to Stopped
first, because Disable Slot is defined only over endpoints that are Stopped or
idle (§4.6.4's note); then PORTSC.PR (§4.19.5); then the slot, and where it
goes is one decision (
reset_recovery::slot_after_offline). A bound disk's goesback from the poll.
bindused to answerfalseanddevice::refuseissuedDisable Slot whatever the quiesce had said; it now answers
Bind::Refused(SlotGoes), and a pair that could not be Stopped leaves its slotwith the port, whose teardown gives it back. A TEST UNIT READY whose recovery
fails goes offline by the same path instead of falling into
refuse.The INQUIRY faults are staged by the bind that takes them, one more than the
budget, so the bind's own disarm has one to take back: the test asserts
bound under 4 staged INQUIRY fault(s): untaken=1.The log tells what happened. A completed round trip that ends a run of
breaks says so where the count is cleared; a recovery whose every step answered
says
Reset Recovery took; the reconfigure prints the two states thecontroller's output context holds after it; a command's timeout prints the
milliseconds it really waited.
Unchanged on purpose: the single-pipe stall paths (BOT §6.7.2, §5.3.3).
Wiring that no committed test judges, only reading: that
scsipassesbroke.event()and notNone; thatdevice::bindacts onSlotGoes::WithTheUnplug; that the two break sites open the budget. Thedecisions they feed are tested in
toyos-xhci. QEMU models no lagging field,no silent controller and no quiesce that fails; the hand probe is the one run
that reached the last two.
What the tests judge, and what QEMU cannot
usb_transport_breakboots three times; the third is round 4's, the T14's ownboot under QEMU — jobcase off the boot stick with the arm, judged by the metal
judge and then on the page's recovery section. The second boot's gate stages, on a disk
that answers every well-formed command: two breaks (one short of the budget,
which the test reads off the gate's own line) by a bad CBW signature, which
QEMU's
usb-storageanswers with a STALL of the Bulk-Out — Bulk-In Running,Bulk-Out Halted; two by a withheld CBW, answered with a STALL of the Bulk-In —
the pair the other way round; then three, the give-up; then three on the
INQUIRY of a disk plugged in over QMP, the give-up inside a bind. The first
boot gains a read whose port reads gone, asserting the
Quiet::Gonearm: theline, and no command, reset or offline after it.
BootOptions::usb_pcaphasQEMU capture the disk's own traffic, and the test requires every Bulk-Only
reset in it to be followed, with nothing between, by a clear to the IN
endpoint and then the OUT one: 6 resets, each with both.
alone: it re-creates an endpoint on an Add whatever the Drop says. Their
judge is the pure crate, which decides every dword of the input context and
whose tests read the flags. The output context calling both endpoints Running
with no doorbell rung is still asserted, and says only that an Add happened;
the Bulk-In found Running and not Stopped at the next break is what the
partial fix (M2) reds on.
of its host. That the clears resynchronise a device, and that a real stick
comes back at all, is judged nowhere in QEMU. BOT §6.6.1's mandatory answer
to an invalid CBW is a STALL of the Bulk-In; QEMU does only the optional
Bulk-Out half. A Stopped pair at a break is not reachable in this driver
without a lying look (every recovery leaves both Running, every break follows
a doorbell) and is covered by the pure model alone. The T14 boot below is
the only judge of the device.
The two checks
Negative control — what was run. On the clean head
a9adf0d7, a guardedscript (refuses a dirty tree or a moved head; applies under a trap that
restores; every replacement must match exactly once and the diff is asserted
non-empty; proves
git status --porcelainempty after) applied each mutationand ran the build, then the judge. QEMU arms:
cargo test --test toyos-build --no-run, thenusb_transport_break -- --nightly; this harness builds thekernel under
--quiet, soCompiling kernelis not printed, and the witness ofthe kernel's own rebuild is its artifact's mtime against a marker touched after
the mutation (1 in every QEMU arm), with no
cargo build failedline. Purearms:
cargo test -p toyos-xhci --no-run, thencargo test -p toyos-xhci.2 withheld CBWs …: read refused=true matched=false untaken=1 healthy=falsebreak 2 of 3 running left the bulk pair ["Stopped", "Halted"]Bulk-Only reset 1 was followed by clears of [Some(02), None]— the wiregive_back_offline_slotsnever called"xHCI: slot 2 disabled" at Some(26078) and "xHCI: port 2 disconnected" at Some(26032)configure::a_recovery_drops_and_adds_both_endpointsreset_recovery's tests,a_context_state_error_is_followed_by_the_command_for_where_the_pipe_wentfirstconfigure::a_recoverys_slot_context_is_context_entries_and_zeroescontext_entries_is_the_higher_index_of_the_paira_halting_completion_code_outranks_a_field_that_still_reads_runningcall's testscall's testsa_slot_goes_back_only_over_a_stopped_pairuntaken=0and never disarms… bound under 4 staged INQUIRY fault(s): untaken=0, wantuntaken=1G1, R1, C1 and R3 are the four the second review found surviving on
502400ba, each build 0 / test 0 there. U6 survived this round's first head(
6555dca5, 0 / 0) and is why the bind is asked for one fault more than ittakes. The whole-change revert (round 2's NC) was not re-run: it is a
hand-written behavioural revert, not a revert of the whole change, and the
review said what it keeps.
Independent oracle. The specification, read and not recalled: xHCI 1.2
§6.2.2.2 and Table 6-7 (the slot context), §4.8.3's note and halt list and
§4.6.9's note (the image and the inference), §4.6.11 (the issue's missing
rung), off the copy the second review fetched; the pure crate's controller
model is written from §4.8.3's state machine and not from the driver. And
QEMU's own capture of the disk's traffic, a second implementation's record of
the wire, for the three requests. Real hardware is requested below and has not
run.
Gates, each with its own command's exit
Dev host, TCG, head
a9adf0d7, tree clean before and after.cargo testintoyos-xhci/, andcargo test -p toyos-xhcicargo test --libredlist::tests::every_row_can_say_what_it_claims: four known-red rows measured 2026-08-20/21 are "more than 31 days ago" as of 2026-09-21.src/redlist.rsis byte-identical toorigin/main's; 310 others pass. Main's, by the calendar; not adjudicated here.cargo test --workspace --exclude toyos-buildtest result: ok, 0 FAILED)cargo clippyshapes ofsrc/clippy.rs, run directlycargo test --test toyos-build usb_transport_break -- --nightlyusb_storage_gate,usb_short_read,usb_boot_stick_pulled,xhci_hotplug,usb_reset_hands_devices_back,usb_disk_index_stable,usb_refused_disk_first,usb_storage_write_error,usb_flush_optional,usb_pool_exhausted,xhci_hid_break,xhci_flap, each-- --nightlyCI. Run 35360971260 on
502400ba:guest (2)andguest-suitered onblocked_dump—PANIC: … src/sched/dump.rs:134:5: the blocked-task dump ran under a lock: preempt depth 2, ALONE: GREEN. That is main's scheduler defect(
issues/kernel/the-blocked-task-dump-panics-when-a-cpu-is-inside-inbox-submit.md),being fixed on #471; this branch touches nothing under
kernel/src/sched.cargo run -- --known-red blocked_dumpsays NOT KNOWN-RED, so the pull requestcannot enter the queue until #471 lands. No CI run exists for
a9adf0d7as thisis written.
log_flush_retryarmsusb-transport-breakwithusb-reset-break, so itruns this path, and is red at a rate on both trees. As the second review took
it, same host and session:
502400ba1,1,0,1,1,0,0,0 — 4 red of 8;78929f12detached 1,1,1,1,1 — 5 of 5. The branch does not raise the rate;n is too small to say it lowers it. One of the head's reds is a shape in none of
the base's five runs and named by no issue:
test-runnerSEGFAULT,execute unmapped address at _start+0x0,[qemu] Init process crashed during boot, notransport line in the excerpt — n=1, unattributed. Not re-measured at
a9adf0d7.--known-red log_flush_retry: NOT ON THE LIST. Nightly tier.The price.
usb_transport_breakboots three times;src/tiers.rs'ci_ms: 5_382and
tests/test-durations' 5408 ms are the hosted price of its first bootalone, and the row says so. No hosted run has priced them all: the name
is Nightly (
Why::TimerAnchored), the pull-request lanes do not run it, and thisPR has no run of its own to take a figure from. Locally the three boots read
PASS usb_transport_break (9s)in round 4's gate run; round 3 measured its twoat 16.9 s, and the difference is not explained here.
Round 4, head
95a090a2Dev host, TCG; exits in
all-exits-usbtransport-4.txt,mut2-exits-usbtransport-4.txtand
ab-*-exits-usbtransport-4.txt. Everything but the last two rows was run at4e39301a;95a090a2changes one judge intests/common/power.rs, and therows it can move were run again on it.
cargo test -p toyos-blackboxcargo test -p toyos-xhcicargo test -p toyos-build --lib95a090a2cargo test --workspace --exclude toyos-buildcargo clippyshapes ofsrc/clippy.rs95a090a2usb_transport_break(three boots now),blackbox_done_chain,blackbox_panic_chain,boot_deadline_ends_a_wedge,hard_lockup_ends_a_deaf_cpu,usb_reset_hands_devices_back,blackbox_foreign_record,blackbox_fault_sealed,machine_reboot,usb_storage_gate,usb_storage_shapes,usb_short_read,usb_boot_stick_pulled,xhci_hotplug,usb_disk_index_stable,usb_refused_disk_first,usb_storage_write_error,usb_flush_optional,usb_pool_exhausted,xhci_hid_break,xhci_flap, each-- --nightlyblackbox_early_panic_sealed4e39301a, by this round's diff: its judge took the line under the report's head for the tail's first record, and it is the recovery section's now.95a090a2fixes the judge; 0 there.usb_reset_records_the_phase_it_cut"usb-wedge: stopping every CPU at the" never reached the boot after the reset) on this round's basea54ce47f(2 of 2) and on #447's own head78929f12(1 of 1), each measured by restoring that tree into this worktree. The name does not exist onorigin/main: it is #447's test, and #447 is open.--known-redsays NOT ON THE LIST.Negative controls, each a checked patch applied and restored by one script,
each tree shown to build:
says_a_breakloses its second clause)cargo test -p toyos-blackboxcargo test -p toyos-blackboxtoclamped to 0)usb_transport_breakno line of the page's recovery section says "transport broke on SCSI 0x2a…"blackbox_done_chain,boot_deadline_ends_a_wedgeA first attempt at the third and fourth removed the kernel's call sites
instead and did not build (
append_recoveryis never used), so its three redsmeasured nothing and are not counted.
Hardware
The T14 disagrees with QEMU, and this does not land on what it said. Three
boots of the
usb-transport-breakarm on the ThinkPad T14 (SanDisk Ultra0781:5581 on the Intel xHC at 00:14.0), 2026-09-21, the images round 3 staged
from
a9adf0d7. Each is one boot; none is a rate.78929f12), controlba034430…usb-storage: read of 1 blocks at 10147 failed on disk 0,root: read of block 1187 failed,test-runnerdied at 3.68 s, the machine sat until the boot deadline ended it at 120117 ms of a 120000 ms bound. It answered ssh after 181 s and the stick enumerated at once.toyos-fat32-checkthen found the log partition corrupt: a long-name entry carrying checksum 0xAC of a short name whose checksum is 0xB0.efc536e8…usb-storagebound, no SCSI command was answered,reset SuperSpeed USB device21 s later, no/dev/sdauntil a physical replug. The base never did that under this arm. What the driver did is not known: the stick held a 29-line loader log and no kernel log, and the black box was cleared by the next image flashed, because a record belongs to the image that armed it.Syncing filesystems...,Rebooting.at 5.808 s,DONE, back in 63 s, the stick fine, every port reset and unpowered in the reset's account. But the sealed record says/log holds this boot to 0 ms and 302 record(s) committed after that reached no volume, the stick carries no kernel log file at all, and the 16-record tail holds no transport line.So on real silicon the new driver is intermittent: one clean survival the base
did not manage in its one boot, and one wedged stick the base did not cause in
its one boot. The owner's standing ruling is that ToyOS never leaves a USB
device in that state, so the branch stays unarmed. Three review rounds under
QEMU found neither outcome: its
usb-storagehas no flash to be inside when areset reaches it, and the QEMU boots of this arm that were looked at recovered in one to two
milliseconds.
Nothing about the wedge is explained yet, and nothing in the driver was changed
on a guess. This round is the instrument.
The instrument. Every sealed record — DONE, WEDGED, PANIC — now carries a
section of its own: every
usb-storage:andxHCI:record from the boot'sfirst transport break on, oldest first, at most 4096 bytes, under a line that
counts what was kept of what the ring held; one line where no transport broke.
It is above the wedge's tail, so the census lines that filled the control's
page (186 records kept from 3.679 s on, 249 older ones dropped, no line of the
break among the kept) cannot push it off, and it never spends the 2048 bytes
kept for the reset's own account. Selection and placement are
toyos-blackbox's and host-tested; the kernel walks the ring for it with thelock-free reader the panic path already uses. It lives outside the driver's
four paths, so the control image carries it too. One measuring line in the
driver: once a quiesce finds both pipes Stopped it says how many TRBs the
controller had not reached on each ring, which tells an abandoned transfer that
completed from one that was cut.
A smaller change would not have done it. Raising the tail's 16 records does not
help a wedge, whose tail is 110 s of census lines, and selecting out of the
rendered tail reads a 32 KiB window of the newest records that the same census
lines fill. This round's first commit message says the break was among run
65's 249 dropped records; what is known is only that it is not among the kept.
Run 69's missing log. The arm abandons the boot's first WRITE(10), and that
write is
logdcreating its file: under QEMUlogdis spawned at 0.301 s, thebreak is at 0.323 s and
logd's first line comes after it.logdends itsvolume for the whole boot on one refused create, with no retry, and says so
only on its console, which the T14 does not have. In run 69 the disk stayed
online to the shutdown (a SYNCHRONIZE CACHE round trip answered at 5.808 s), so
the create was answered with an error by a disk that was still there; which
error is not known, and the section will say. Filed:
issues/boot-media/logd-ends-the-boots-log-on-one-refused-create-and-nothing-durable-says-so.md.It is
logd's policy and outside this branch's fence.Run 66, as hypotheses. 254 s is the 120 s deadline, about 74 s, and the 60 s
runs 65 and 69 spent outside the kernel: the stick was already wedged when the
firmware went to load the loader off it. Ranked, each with the record that
would confirm it:
quiesce's Stop Endpoint can land inside the abandoned transfer; the one QEMU
boot looked at read 0 TRBs unreached on both pipes. Confirmed by
endpoint 4 is Stopped with 1 TRB(s) … not reachedin a wedged boot against0 TRB(s)in a clean one.transfer abandoned on EP0, the later requests unsent and the port reset
written with no settle. Confirmed by
timed out after <N> mswith N under2000,
would not take a Bulk-Only Reset,not issued: this call's budget after a break is spent, or an offline line readingStopped=falseorreset=false.still open. Ranked last: six staged cuts of that kind (runs 39–47) never cost
this stick. Confirmed by a section ending in
completed afterandusb-quiesce: a Bulk-Only command was open in its … phase.Requested (
t14-request-usbtransport-4.txt): an instrumented control and aninstrumented head, staged by one script from
4e39301a(the head,95a090a2, differs from it by one judge intests/common/power.rs).47e3783ca63ba5c29b61b04ac09387e929070b4c616407f8cede443b00ef54a90c7cab1fded271e317889b2bbc58ef3d38f3a27750b7aef14eb279150d90e943Control twice, then the head up to five times, stopping once one wedged-stick
record and one clean record have both been read. A wedged stick's record is
still in DRAM: the owner replugs the stick and the same image is flashed again,
whose first loader pass prints the record and boots no kernel. Any other image
clears it.
What no boot of this arm can show is still the recorded failure: run 55's CSW
two seconds late, then Babble and USB Transaction Error alternating.
Q2 — why the first boot after a replug passes and later ones fail
The reboot path leaves the xHCI clean (
stop.rs: PR on every connected port,HCRST, PP clear, bus mastering off). The stick is what carries state: the
T14's ports never lose VBUS. On the record, a boot that hung left its stick
inside a command with one pipe halted from 5.556 s until the platform reset at
252 s, and the stick then needed a replug. That a bus reset did not clear what
that stick was left in is what the replug showed; what state that was is not
measured.
Q3 — the deadline that fired 132 s late
#462's file attributes the lateness to cpu0's recovery loop (
timer=9 xhci=1729). That census is from 3.47 s; the loop ended at 5.556 s. My reading:with logd, netd and the runner dead and init parked, every CPU planned
TimerPlan::Stopand halted, anddeadline::pollruns from the timer entryonly — so the bound fired on the first interrupt anything raised after 120 s.
Outside this change's path, not fixed here.
Filed, not fixed
issues/kernel/a-stick-that-answers-late-is-broken-by-the-two-second-abandon.md— the first break in both records is a CSW two seconds late. It quotes its
evidence now, and states what it infers as inference.
issues/kernel/a-disk-taken-offline-is-never-brought-back.md— the rung pastoffline, a device reset that keeps the disk.
issues/boot-media/logd-ends-the-boots-log-on-one-refused-create-and-nothing-durable-says-so.md— run 69's missing log.
issues/build/the-metal-loop-writes-the-readback-volume-into-a-directory-it-has-not-made.md— run 69's readback, lost after the boot was spent.
Fence: kernel,
toyos-xhci,toyos-blackbox, the harness, the metal profileand four issue files. No file under
toyos-abi/,toyos/oruserland/libc/.🤖 Generated with Claude Code
https://claude.ai/code/session_014iqcj4jDKpaiDX8B7CMvmK