docs: the removal lifecycle closes — three triggers, one path (S5)

Storage removal is now robust to all three ways a volume can leave, and the docs
say so. storage-architecture.md and storage-design-rationale.md move medium_changed
from "planned to be consumed" to consumed, and record the third trigger:

  - the DEVICE leaving the tree (a pulled stick)          — presence polling
  - the MEDIUM leaving while its device stays (a reader)  — the volume manager
    now consumes the pushed medium_changed event
  - the storage DRIVER crashing while its device stays    — a channel-liveness
    geometry() probe reaps the volume and rebuilds it on the restarted driver's
    fresh channel; presence polling alone cannot see this (the V4 open edge)

The re-adopt-and-remount path is QEMU-proven by the driver-crash rebuild; a
physical unplug/replug is bench-verified (QEMU cannot re-present a usb-storage
device_add). The transport-native eject signal (SCSI UNIT ATTENTION, AHCI
PxSSTS, NVMe namespace-change AER) in place of the TEST UNIT READY poll stays
the documented future refinement.
This commit is contained in:
Daniel Samson
2026-08-10 06:08:56 +01:00
parent 061eb7c004
commit 4dfb5012c0
2 changed files with 42 additions and 33 deletions
@@ -209,18 +209,23 @@ matrix-proven shape; genuinely open.
names it as the 256-byte ceiling's unlock — Fuchsia's FIFO+VMO is the
precedent); format-level crash honesty (a Power-Safe-style journaling or COW
filesystem) once danos outgrows FAT; per-process namespaces.
7. **The media-presence event** (settled in principle; lands with the volume
manager): the block protocol gains a pushed event — `medium_changed`, with
present/absent and a change counter — produced by the storage driver from
its transport's native signal (SCSI UNIT ATTENTION / TEST UNIT READY for
USB and ATAPI, PxSSTS for AHCI, namespace-change AER for NVMe) and
consumed by the volume manager, which runs the SAME kill-retire-remount
path it runs on channel death — one lifecycle, two triggers. The driver
reports presence, never content; a pushed event carries no capability,
which the kernel already guarantees. The device staying while its medium
leaves is the one removable-media case the channel-death trigger cannot
see; without this event a swapped SD card would be served with the old
card's filesystem state.
7. **The media-presence event** (the consuming half is BUILT; the
transport-native signal stays future): the block protocol carries a pushed
event — `medium_changed`, with present/absent and a change counter —
produced today by the storage driver from a TEST UNIT READY poll (the
transport's native signal — SCSI UNIT ATTENTION, PxSSTS for AHCI,
namespace-change AER for NVMe — is the future refinement in place of the
poll) and now **consumed** by the volume manager, which subscribes per
device and runs the SAME kill-retire-remount path it runs on channel death.
The driver reports presence, never content; a pushed event carries no
capability, which the kernel already guarantees. The device staying while
its medium leaves is the one removable-media case the channel-death trigger
cannot see; without this event a swapped SD card would be served with the
old card's filesystem state. A THIRD trigger closes the last gap — a
storage driver that *crashes* while its device stays present: channel death
there is invisible to presence polling, so the volume manager probes channel
liveness (`geometry()`) each tick and reaps-then-rebuilds the volume on the
restarted driver's fresh channel. One lifecycle, three triggers.
8. **Volume identity, and the mount map as danos's fstab** (settled). The
lesson is Linux's own history: fstab keyed on `/dev/sda1` for years and
broke whenever a drive changed ports or enumeration order; `UUID=` entries