55 lines
3.7 KiB
Markdown
55 lines
3.7 KiB
Markdown
# The hot-plug matrix: unplug anything, replug anywhere
|
||
|
||
*Status: COMPLETE, 2026-08-09, same day — H1..H5 all green, full suite 124/124.
|
||
The predicted cascade bug was real and found by H2: `tearDownPort` did not recurse
|
||
into a hub yanked from a root port, so its subtree stayed live against vanished
|
||
hardware and the replugged hub found its port occupied; fixed by mirroring
|
||
`tearDownHubDevice`'s children-first recursion (77e7001). Every other cell passed
|
||
against the establishment-track machinery unchanged.*
|
||
|
||
*2026-08-09. The requirement, verbatim: "i want to be able to unplug devices and
|
||
plug them back in, in any order and on any hub." The machinery exists end to end —
|
||
per-device driver processes, cascading teardown (`tearDownHubDevice` recurses a
|
||
departing hub's subtree, children first), reap-on-removal in the manager, idempotent
|
||
per-port registration identity, lineage rebinding — but only one cell of the matrix
|
||
is verified: a leaf keyboard behind a hub, unplugged and replugged on the same port
|
||
(`usb-hub-unplug`). This plan verifies the rest and fixes what verification flushes
|
||
out. The cascade path has never executed; expect it to carry at least one bug.*
|
||
|
||
**Out of scope, stated up front:** unplugging the BOOT stick (the drivers handle it;
|
||
fat holds a dead block channel until M21 remount gives it re-acquisition), and
|
||
real-hardware root-port timing (the Ryzen bench is the acceptance run for that, as
|
||
always — QEMU proves the logic, not the silicon).
|
||
|
||
## The discrimination lever
|
||
|
||
Every case's rebind tail (— reaping → delegated → second-generation `ok`) is
|
||
impossible without the manager's reap: stashing the reap out of
|
||
`onChildRemoved`/`pruneChildrenOf` makes every matrix case fail at the dedupe wall.
|
||
That is the standing discrimination check for the whole matrix — run it once per
|
||
case shape, not per commit.
|
||
|
||
## The matrix
|
||
|
||
| Case | Topology | Event sequence | What it proves |
|
||
|---|---|---|---|
|
||
| H1 `usb-root-replug` | keyboard on a 2nd controller's ROOT port | del @8s, add @14s (same port) | the `tearDownPort` path + reap + rebind; root-port changes arrive via the 250 ms reconcile tick (QEMU raises no root-port events) |
|
||
| H2 `usb-hub-yank` | hub on 2nd controller; keyboard AND mouse behind it | del the HUB @8s; re-add hub @14s, kbd @17s, mouse @18s | the recursive cascade: one event removes the subtree, EVERY bound driver is reaped, and the rebuilt hub re-binds both |
|
||
| H3 `usb-hub-nested-yank` | hub → hub → keyboard | del the OUTER hub @8s; re-add all three @14–18s | the cascade recursion depth ≥ 2, and a nested rebuild |
|
||
| H4 `usb-replug-moved` | keyboard behind hub port 1.1 | del @8s; add on port 1.2 @14s | a replug on a DIFFERENT port is simply a new device: new port identity, new id, fresh match — no stale state ties a driver to the old port |
|
||
| H5 `usb-replug-cycles` | keyboard behind hub | del/add three times (6 hooks) | no state leaks across generations: slots, the bus's open table, driver entries (reaped entries must be reusable) |
|
||
|
||
Ordered expects follow the `usb-hub-unplug` shape: the boot devices' `ok` lines all
|
||
precede the first unplug, so a tail of `removed → reaping → delegated → ok` can only
|
||
be satisfied by the generation the sequence created. H2/H3 require one `reaping` per
|
||
bound child; H5 requires three.
|
||
|
||
## Order and discipline
|
||
|
||
H1 → H2 → H3 → H4 → H5, one commit per case (plus its fix if it finds a bug — the
|
||
case and the fix land together, the case having failed first). The standing
|
||
discrimination run once for H1 and once for H2's shape. Full suite green at the end,
|
||
docs touched where behavior was corrected, memory updated. Fixes stay within the
|
||
settled design: teardown order (children first), reap-on-removal, identity per port
|
||
— anything design-shaped that surfaces stops the loop and asks.
|