docs(init-nsmgr): add co-victim audit — acpid was the only active trigger

Systematic audit of all 18 scheme-serving daemons for the acpid failure
class (single-threaded daemon that can wait unboundedly in its serving
thread). Findings recorded in INIT-NAMESPACE-MANAGER-SCALABILITY-PLAN.md:

- acpid was the ONLY software unbounded wait in the boot-critical path
  (fixed). No other boot-path handler has an unbounded software wait or a
  reentrant blocking open of another scheme.
- Hardware busy-waits (rtcd/ps2d/audio/gpu register polls) are
  hardware-bounded or in daemons pcid does not spawn without the device.
- ucsid is the one remaining co-victim: it reads /scheme/acpi in
  build_state() before publishing its scheme and is the only blocking,
  acpi-dependent boot unit in redbear-mini. It already degrades on EAGAIN
  and works with acpid fixed. It must NOT be flipped to oneshot_async
  (that breaks ucsi scheme registration, which init performs via the
  {scheme=…} type); the correct hardening is a source refactor
  (publish-then-discover), done with runtime validation, not blind.
This commit is contained in:
2026-07-21 05:26:52 +09:00
parent 18c892a136
commit 9f1a10937f
@@ -76,6 +76,37 @@ via `lseek(0)`+`read`. A per-open trace in `initnsmgr` (to a debug fd from
`UPPER_FDTBL_TAG + GlobalSchemes::Debug`) pinpointed the wedged open as `acpi:processor/CPU3/pss`
(then, post-fix, `display.vesa:v2/2`).
## Co-victim audit (2026-07-21): other daemons on the same chain
A systematic audit of all 18 scheme-serving daemons for the acpid failure class (a single-threaded
daemon that can wait unboundedly in its serving thread) found:
- **acpid was the only *software* unbounded wait** in the boot-critical path (the AML mutex bug —
fixed). No other boot-path scheme handler contains an unbounded software wait or a reentrant
blocking open/read/call of another scheme.
- **Hardware busy-waits** (`rtcd` RTC UIP bit, `ps2d` PS/2 `OUTPUT_FULL`, `sb16d`/`ihdad` DSP/RIRB,
`ihdgd`/`ahcid`/`nvmed`/`bcm2835` register polls) are either hardware-bounded (the bit clears in
microseconds) or live in daemons that pcid does not spawn without the matching device (no audio on
QEMU q35, etc.). Not the acpid class. (They are still a robustness item under the project WARNING
POLICY — unbounded on paper — but adding timeouts to ~15 sites blind, without a reproducing hang,
risks regressions and is out of scope here.)
- **`ucsid` is the one remaining co-victim worth noting.** It consumes `/scheme/acpi/*` in
`build_state()` *before* it publishes its scheme, and in `config/redbear-mini.toml` it is the only
boot-critical, acpi-dependent unit with a **blocking** init type (`type = { scheme = "ucsi" }`,
while every other acpi consumer there is `oneshot_async`). Before the acpid fix, a stalled acpid
would have hung ucsid's init and thus boot. It already degrades gracefully on `EAGAIN`
(`discover_ucsi_devices` returns empty), but `EAGAIN` only covers "acpid is busy", not "acpid is
wedged" (open blocks, per the kernel corollary below). With acpid fixed it works.
**Do not simply flip ucsid to `oneshot_async`** to satisfy the "optional hardware must not block
boot" rule (`bare-boot-blocking-init-units`): `ready_sync_scheme` hands the scheme cap fd to init
via the INIT_NOTIFY pipe (`daemon/src/lib.rs:130``ready_with_fd`), so init registers the `ucsi`
scheme *on the daemon's behalf* — only the `{scheme=…}` init type does that. `oneshot_async` would
leave `ucsi` unregistered (consumers get `ENODEV`). The correct hardening is a source refactor:
publish the scheme immediately with a lazy/empty state, then run the heavy ACPI/i2c discovery in
the background after `ready_sync_scheme`. It is defense-in-depth against an already-fixed trigger,
not an active bug, so it must be done with runtime validation, not blind at session end.
## The kernel corollary — `open` ignores `O_NONBLOCK`
The manager cannot even *defer* a slow open today, because in Redox an `open()` with `O_NONBLOCK`