Artemis Milestone 2h (foundational): sync wait bridge + SCSI READ CAPACITY(10)
Closes the gap block_subsystem.c needs before any of 2h's real work (blkio_usb.c, attach wiring, hot-detach) can start: this driver is fully async/polled with no way for a synchronous caller (blkio_read()/ blkio_info() etc.) to get a result back. xhci_bot_wait_for_idle() is a bounded busy-wait over xhci_poll_events() -- MUST be called only from outside xhci_poll_events()'s own call frame, never from within it or a next_action dispatch (recursion into live Event Ring/ERDP processing, same class of hazard already documented for doorbell rings in this driver). xhci_get_dev() exposes the module-static device handle to outside callers that didn't observe the original hotplug event. SCSI READ CAPACITY(10) (opcode 0x25) is the other half -- nothing could learn a device's block size/capacity before this. First attempt sent it bare and hit the classic first-command UNIT ATTENTION (CSW FAILED); fixed with the same TUR-guard pattern READ(10) already used, generalized via a new bot_tur_chain_target field so TEST UNIT READY's PASS handling can chain into either command. bot_data_buf grown 512->1024 bytes (one Forth block = two 512-byte SCSI blocks, per block_subsystem.c's own 1KiB-unit convention). Verified live via a temporary probe (hot-attached disk/usb-thumbdrive- test.img via QMP, reverted after capture): TUR-guarded READ CAPACITY10 correctly reported last LBA=0x1ffff, block size=0x200 -- exactly 64MiB, matching the test image byte for byte -- followed by a TUR-guarded 1024-byte/2-block READ10, both PASS. All three architectures re-verified clean, probe-free boot to ok> on the reverted tree. FABRIC-2.md Section X 2h updated with the writeup; the blkio_usb.c backend itself is next. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
65effbd1ba
commit
d686f28853
+70
@@ -3724,6 +3724,76 @@ function. Detaching should be safe without renumbering other devices' LBNs as lo
|
||||
the tail of the chain (true today; would need reconsidering if a second hot-pluggable device
|
||||
class is ever added), but the function itself does not exist yet.
|
||||
|
||||
**2h foundational piece — synchronous bridge + READ CAPACITY(10), done 2026-08-25.** Before any
|
||||
of the three items above can be built, something has to close the gap between this driver
|
||||
(fully async, single-outstanding-transaction, polled from `sk_repl_idle()`) and
|
||||
`block_subsystem.c` (ordinary synchronous function calls — `blkio_read()`/`blkio_info()` etc.
|
||||
have no way to "come back later" for a result). `xhci_bot_wait_for_idle(dev, max_iters)` is
|
||||
that bridge: a bounded busy-wait that calls `xhci_poll_events()` in a loop until
|
||||
`dev->bot_cmd_kind` returns to `BOT_CMD_NONE`, returning `dev->bot_last_status`
|
||||
(`BOT_STATUS_PASS`/`BOT_STATUS_FAILED`/`BOT_STATUS_TIMEOUT`). Every BOT command's CSW-received
|
||||
handling now resets `bot_cmd_kind`/sets `bot_last_status` on every terminal path — including the
|
||||
two CSW signature/tag-mismatch discard branches, which previously left `bot_cmd_kind` stuck
|
||||
forever on a garbled CSW (harmless before, since nothing read it back; would have hung this new
|
||||
wait loop for its full `max_iters` on the same condition).
|
||||
|
||||
**Load-bearing constraint, caught by advisor review before writing any code:** this wait
|
||||
function must never be called from inside `xhci_poll_events()` itself, or from anything it
|
||||
calls (a `next_action` dispatch, a Transfer Event handler) — the busy-wait's own repeated
|
||||
`xhci_poll_events()` calls would recurse into live Event Ring/ERDP processing, the same class of
|
||||
hazard this driver's `next_action` deferral mechanism exists to avoid for doorbell rings (see
|
||||
`xhci_dev_t`'s own doc comment on `next_action`, and the confirmed-live amd64 hang recorded
|
||||
there). A new `xhci_get_dev()` accessor (returns the module-static `xhci_dev_t*` this driver
|
||||
already latches internally, or NULL if not brought up) lets an outside caller — this increment's
|
||||
temp probe used `sk_repl_idle()`, immediately after its own `xhci_poll_events()` call had already
|
||||
returned — reach the device handle safely; every earlier caller of this driver's public API
|
||||
already had one in hand (`kernel_main.c`'s local `xhci_dev_t`), which doesn't help code that only
|
||||
runs later, on a hotplug event it wasn't the one to observe.
|
||||
|
||||
SCSI READ CAPACITY(10) (SBC-3 section 5.14, opcode `0x25`) is the other half: `blk_subsys_attach_device()`
|
||||
reads `info.total_blocks` (via `blkio_info()`) before it does anything else, and there was no way
|
||||
for this driver to learn a device's block size/capacity at all until now.
|
||||
`xhci_bot_send_read_capacity10()` mirrors `xhci_bot_send_read10()`'s CBW-build shape (opcode +
|
||||
all-zero CDB otherwise, 8-byte Data-In reply); on a PASS CSW the reply's big-endian last-LBA and
|
||||
block-length fields are parsed into `dev->bot_cap_last_lba`/`bot_cap_block_size`. First attempt
|
||||
sent it bare (no TEST UNIT READY guard) and it failed — CSW status FAILED, residue 8 — the exact
|
||||
first-command UNIT ATTENTION READ(10) used to eat before `xhci_bot_read_block()` existed, caught
|
||||
immediately by the live capture rather than assumed away. Fixed the same way READ(10) was: a new
|
||||
`bot_tur_chain_target` field (`BOT_TUR_CHAIN_READ10`/`BOT_TUR_CHAIN_READ_CAPACITY10`) lets TEST
|
||||
UNIT READY's own PASS handling tell which command to chain into next — `xhci_bot_get_capacity()`
|
||||
is the new TUR-guarded entry point (sets the chain target, resets the retry counter, issues TUR
|
||||
first), matching `xhci_bot_read_block()`'s own shape exactly.
|
||||
|
||||
`bot_data_buf` grew from 512 to 1024 bytes — `block_subsystem.c`'s own header comment says
|
||||
blkio backends operate on 1 KiB units, i.e. one Forth block is two consecutive 512-byte SCSI
|
||||
blocks, and 512 bytes could never hold that.
|
||||
|
||||
Verified live via a temporary probe (written, run once, log captured, then reverted per this
|
||||
project's own probe convention — hot-attached `disk/usb-thumbdrive-test.img` through the running
|
||||
instance's QMP socket, same as every earlier BOT capture), amd64 only (the mechanism is
|
||||
architecture-independent code already proven identical across all three arches by every earlier
|
||||
BOT increment, same reasoning as the TEST UNIT READY increment's own capture scope): full chain
|
||||
`CBW (TEST UNIT READY)` → `FAILED` → retry → `PASS` → `unit ready -- issuing READ CAPACITY10` →
|
||||
`CBW (READ CAPACITY10) submitted` → `CSW status = PASS` → `READ CAPACITY last LBA=0x0001ffff` /
|
||||
`block size=0x00000200` → `probe READ CAPACITY10 PASS`, immediately followed by a second
|
||||
TUR→READ(10) chain (`xhci_bot_read_block(dev, slot, 0, 2, 512)`, exercising the grown buffer)
|
||||
also `PASS`. `0x1ffff` (131071) × `0x200` (512) = exactly 64 MiB, matching
|
||||
`usb-thumbdrive-test.img`'s real size byte for byte — proof this isn't just a well-formed reply,
|
||||
but the *correct* one. `logs/20260825-120043/amd64/` (first attempt, READ CAPACITY10 bare,
|
||||
FAILED — kept as evidence of the bug this increment's own TUR-guard fix addresses, not deleted
|
||||
as a failed run), `logs/20260825-121339/amd64/` (fixed, full PASS chain). The probe (a
|
||||
`bot_probe_pending`/`bot_probe_slot_id` flag pair set by the SET_CONFIGURATION handler and
|
||||
consumed in `sk_repl_idle()`) was removed after capture; `xhci_bot_wait_for_idle()`,
|
||||
`xhci_get_dev()`, `xhci_bot_send_read_capacity10()`, and `xhci_bot_get_capacity()` remain as
|
||||
permanent, reusable substrate for the next 2h increment (the actual `blkio_usb.c` backend).
|
||||
Re-verified probe-free afterward, all three architectures, clean boots with no BOT activity:
|
||||
`logs/20260825-122016/amd64/`, `logs/20260825-122318/aarch64/`, `logs/20260825-122527/riscv64/`.
|
||||
|
||||
Still ahead for 2h: the `blkio_usb.c` backend itself (per `blk_format_or_load_disk()`'s own
|
||||
"NEVER writes to disk here" discipline at attach time, a read-only backend is sufficient to land
|
||||
first — WRITE(10) doesn't exist in this driver yet and isn't a prerequisite), the connect-time
|
||||
call site wiring a ready `blkio_dev*` into `blk_subsys_attach_device()`, and the hot-detach path.
|
||||
|
||||
### Milestone 3 — Block subsystem extensions (Section U items 3-6, Section V area A)
|
||||
|
||||
Depends on Milestone 2 existing (needs a real device to test against, though the
|
||||
|
||||
Reference in New Issue
Block a user