docs(persistence): document module and refine FAT recovery to two cases

Record that persistence is now implemented and hardware-verified, and correct
the crash-recovery rule everywhere it appeared as "just promote the tmp": a
crash during the tmp write leaves a partial tmp, so recovery keeps the
committed target when both files are present and only promotes the tmp when
the target was already unlinked. Also fix the stale README heading that still
said the SD was on shared SPI2.
This commit is contained in:
Julien Calixte
2026-07-11 12:02:30 +02:00
parent 1e3220a95f
commit 7a1f25f5d1
4 changed files with 46 additions and 19 deletions

View File

@@ -39,10 +39,18 @@ as-is on FAT; the destination must be unlinked first.
That unlink opens a crash window: between `f_unlink(target)` and `f_rename`, the
target is momentarily gone while `*.tmp` holds the complete, fsync'd new content.
The consequence for the real persistence module is a **boot-recovery** rule: a
lingering `*.tmp` at startup means the last save didn't finish → promote it
(it's the newest complete copy). The spike now does unlink-then-rename and
tolerates a missing target, so re-runs are idempotent; recorded in
The consequence for the real persistence module is a **boot-recovery** rule for a
lingering `*.tmp`. Implementing it (`firmware::persistence::Storage::recover`)
surfaced that "promote the tmp" is too blunt: a crash *during* the tmp write (a
second window, before the fsync) leaves a **partial** tmp, so recovery must look
at whether the target survived —
- **tmp + target both present** → crash point ambiguous, tmp may be partial →
keep the committed target, discard the tmp (the "previous version" guarantee);
- **tmp only, target absent** → target was already unlinked → tmp is the newest
complete copy → promote it.
The spike now does unlink-then-rename and tolerates a missing target, so re-runs
are idempotent; recorded in
[ADR-007](../adr.md#adr-007-storage-split--fat-on-sd-for-working-copy-littlefs-on-flash-for-config).
## Summary