115 lines
7.0 KiB
Markdown
115 lines
7.0 KiB
Markdown
# M10 shutdown validation
|
||
|
||
Development reference and historical context. For current operating instructions, use the [user manual](../README.md). Acceptance applies only to the source and artifacts identified in each record.
|
||
|
||
|
||
[Shutdown usage](power.md) · [Implementation ledger](implementation-status.md)
|
||
|
||
M10 software acceptance passed on 2026-09-21 in isolated ARM virtual machines.
|
||
The complete shutdown suite, CLI packaging and earlier-milestone regressions
|
||
passed. Physical Pi timing, flash-controller durability,
|
||
power rails and battery behavior remain deferred.
|
||
|
||
## Verified shutdown behavior
|
||
|
||
The ordinary FDS user can request `fds poweroff` or `fds reboot`. New cartridge
|
||
operations freeze before the daemon stops the desktop, managed programs and
|
||
networking. The original DATA filesystem descriptor checks writeback, the
|
||
filesystem becomes read-only, a second check catches remount writeback, and normal
|
||
unmount completes before native s6 stops the remaining services and halts or
|
||
reboots. Dasung remains supervised until that final service stop.
|
||
|
||
`make power-test` passed all six cases in `out/m10-vm.tituqzh7/`:
|
||
|
||
| Case | Observed result | VM preparation-to-kernel-action time |
|
||
| --- | --- | --- |
|
||
| Idle | Ordinary-user poweroff; unprivileged internal hook refused | 540.733 ms |
|
||
| Active workload | GUI, DHCP and TERM-ignoring descendants stopped; DATA verified; actual reboot | 1835.329 ms |
|
||
| Direct native poweroff | Busy DATA blocks the native hook until resolved; cancellation refused | 414.091 ms |
|
||
| Active media preview | Shutdown and new confirmation refused; cancellation leaves disk unchanged | 289.099 ms after retry |
|
||
| Crashed DATA service | Same insertion stays quarantined across further restarts; no false SAFE | 281.307 ms after removing fixture |
|
||
| Attached-device writeback error | Real EIO retained across restarts; SAFE and shutdown refused | 344.777 ms after removing fixture |
|
||
|
||
These are measurements inside generic QEMU with the Pi kernel, not Pi or
|
||
physical power-off benchmarks. They do not establish the sub-second idle or
|
||
1.5-second normal-DATA hardware targets. The workload deliberately ignores TERM,
|
||
so the manager must wait its bounded termination deadline and confirm actual
|
||
exit before proceeding.
|
||
|
||
Each case records ordered `CLOCK_BOOTTIME` events and captures the final native
|
||
shutdown record. The test waits for the kernel's halt/reboot message and QEMU's
|
||
successful exit; closing the emulator does not count as a completed shutdown.
|
||
The full command log is `out/logs/m10-final-power.log`.
|
||
|
||
## DATA integrity and failure handling
|
||
|
||
The workload writes at least 128 MiB through a rotating 64 MiB file. After reboot,
|
||
independent host `e2fsck` finds a clean filesystem, SHA-256 matches all 64 MiB, and
|
||
an ordinary-user marker retains its exact contents. Open writable descriptors
|
||
and a shell working directory both block shutdown. Explicit resume enables new
|
||
operations but does not recreate stopped jobs.
|
||
|
||
Before writable activation, a root-private insertion record is created under
|
||
`/run/fds/data-sessions`. A service crash cannot discard the original descriptor's
|
||
writeback history and then issue SAFE based on a fresh descriptor. The same
|
||
insertion becomes read-only and quarantined; a known error retains its diagnostic.
|
||
Records survive service restarts during this boot, not power loss or a different
|
||
insertion. The separate DATA regression passed in `out/m7-vm.vmhkmyqk/`.
|
||
|
||
The writeback test uses QEMU's `blkdebug` to return persistent EIO for writes to a
|
||
known allocated file block. GPT, manifest and journal metadata remain outside
|
||
that injected sector. The device stays attached; Linux reports a real writeback
|
||
error and `syncfs` fails. The daemon refuses eject, keeps the fault after restart,
|
||
and blocks shutdown through another restart. No filesystem-cleanliness claim is
|
||
made for this deliberately damaged image. Removing the test fixture merely lets
|
||
the remaining system shut down; it never turns the failed insertion SAFE.
|
||
|
||
See [QEMU's fault-injection documentation](https://www.qemu.org/docs/master/devel/testing/blkdebug.html).
|
||
A one-shot fault is insufficient evidence by itself because the guest may retry
|
||
it successfully; the acceptance case verifies the actual guest-visible EIO.
|
||
|
||
## Native guard and dependencies
|
||
|
||
The native shutdown hook remains alive while preparation is blocked, including
|
||
when an administrator calls the upstream `poweroff` directly. A nonzero hook
|
||
exit would let native shutdown continue, so watcher failure uses a blocked
|
||
signal wait. Normal waiting uses inotify and process/cgroup exit events, with no
|
||
sleep-based assumptions. Once native shutdown starts it cannot be cancelled.
|
||
|
||
`fds-power` is a static alias of `fds`. The CLI now directly declares the already
|
||
locked `libc` crate for inotify, identity and signal operations. No new external
|
||
crate or target package is introduced. `fds-init` explicitly depends on
|
||
`fds-cartridged`, since the native hook requires its DATA checks. DHCP and its
|
||
privilege-separated children now share a managed cgroup. The final native grace
|
||
period is zero after managed exits and DATA verification have completed.
|
||
|
||
## Regression evidence
|
||
|
||
The development archive passed rootfs rejection checks, native PID 1/service
|
||
control and actual poweroff, desktop/network transitions, tooling/static-ELF
|
||
checks and `make check`. Logs are `out/logs/m10-final-*.log`; desktop evidence is
|
||
`out/m8-vm.1c20rcpb/`, native init is `out/m2-vm.5ARJNL/`. The final CLI image also passed native init (`out/m2-vm.ysuhZ5/`), all nine
|
||
stage0 cases (`out/m4-vm.e3SBsX/`), and the ordinary-user console checks
|
||
(`out/m5-vm.4l796uhp/`). Cartridge regression passed in `out/m6-vm.p7gfwnj2/`, DATA regression in
|
||
`out/m7-vm.82fqc9wa/`, and confirmed media writes in `out/m9-vm.5a58kou6/`.
|
||
|
||
The development runtime tested here is `out/rootfs-build.WEtGnr/`, with
|
||
191 packages and 1406 AArch64 ELF files. Its rootfs SHA-256 is
|
||
`9de03100b998cac2a754fc8e05acbad0bd3f45846dd637ba4c597ea3ba61f5bb`.
|
||
Its SYSTEM is `out/system-build.dd3e74_2/system.img`, SHA-256
|
||
`bef3b01bc8bc68ca673aa6a0ec73892ddaa2901864eed204745749915ee34b18`.
|
||
The subsequent explicit init-to-cartridge package dependency was verified in the
|
||
final CLI build; the development archive already includes that daemon through
|
||
its base package. Runtime binaries and service behavior are identical.
|
||
The CLI archive has 180 packages and 1394 AArch64 ELF files, and is 499527680 bytes.
|
||
|
||
Kernel-to-console was 1821.039788 ms versus M9's 1854.858231 ms: **−33.818443 ms**.
|
||
The actual ARM comparison passed without a regression exception; see
|
||
`out/logs/m10-boot-comparison.log`. This is a VM observation, not a Pi target result.
|
||
|
||
| Artifact | Location | SHA-256 |
|
||
| --- | --- | --- |
|
||
| CLI rootfs | `out/rootfs-build.0AzReg/rootfs-aarch64.tar` | `ebfc58171aeee6f737c2a93a0f3a318d4f02eb915edb3b5a3cf0f51848863930` |
|
||
| CLI SYSTEM | `out/system-build.c7q19iil/system.img` | `81dc8083777b497f02a290a15eeb246465689d0deaff97d761aeb882d140b44b` |
|
||
| FAT32 boot partition | `out/boot-build.42abbf_h/boot.fat` | `0972b5973d1fefc377ec4c800043fb8e4aa3013140f95218cdebcf38dcd22a49` |
|