v0.13.22 — replace bisect-on-fail with hysteresis Block↔Single
The v0.13.21 bisect-on-fail recovery was correct (100% of recoverable
sectors picked up) but slow on dense damage clusters. Live test on
Dune 2 v0.13.21 burned ~30 s per damaged 60-block — paying a ~5 s
kernel ABORT/timeout at every level of a log₂(60) ≈ 6 deep DFS, on
the failing branch each time.
Replaced with a two-state hysteresis machine in Disc::copy:
Block(batch):
read(batch) ok → write, advance, stay Block
read(batch) fail → switch to Single, retry SAME range at bpt=1
Single:
read(1) ok → write, consecutive_good++
if consecutive_good >= BPT1_EXIT_THRESHOLD:
switch to Block, reset counter
read(1) fail → mark NonTrimmed, consecutive_good = 0
BPT1_EXIT_THRESHOLD = 10_000 sectors (= 20 MB clean run). Calibrated
from the 2026-04-26 BU40N empirical probe data; tunable.
Per-block math on a damaged 60-block with 1 truly bad sector:
Bisect (v0.13.21): ~30 s (5 s × 6 levels)
Hysteresis (v0.13.22): ~10 s (5 s bpt=batch fail
+ 59 × 1 ms good
+ 1 × 5 s bad)
Inside a damaged cluster spanning many 60-blocks the win compounds:
hysteresis pays the bpt=batch fail cost ONCE on entry, then stays at
bpt=1 across the cluster; bisection re-paid it every 60 sectors. For
Dune 2's ~1248-sector boundary cluster that's ~21 fewer 5-sec
kernel timeouts ≈ 100 s saved per pass.
Telemetry: new phase=mode_change trace event with from, to, lba, and
consecutive_good. Replaces v0.13.21's phase=bisect. Worklist DFS is
gone — single iterative for s in 0..count on the failure path.
Test rename, same fixture and same 100% recovery expectation:
test_disc_copy_bisect_recovers_via_single_sector_reads
→ test_disc_copy_hysteresis_recovers_via_single_sector_reads
Also adds DamageSeverity (Clean / Cosmetic / Moderate / Serious) +
classify_damage(bad_sectors, lost_ms), re-exported from libfreemkv,
so applications can render structured severity instead of formatting
their own from raw counters.
This commit is contained in:
@@ -1,5 +1,51 @@
|
||||
# Changelog
|
||||
|
||||
## 0.13.22 (2026-04-26)
|
||||
|
||||
### Replace bisect-on-fail with hysteresis state machine (Block ↔ Single)
|
||||
|
||||
Live test on Dune 2 v0.13.21 showed bisect-on-fail recovered every
|
||||
recoverable sector, but spent ~30 sec per damaged 60-block (paying a
|
||||
~5 sec kernel timeout at every bisection level). Each level descended
|
||||
log₂(60) ≈ 6 times on the failing branch.
|
||||
|
||||
Replaced with a two-state hysteresis machine in `Disc::copy`:
|
||||
|
||||
```
|
||||
Block(batch):
|
||||
read(batch) ok → write, advance, stay Block
|
||||
read(batch) fail → switch to Single, retry SAME range at bpt=1
|
||||
|
||||
Single:
|
||||
read(1) ok → write, consecutive_good++
|
||||
if consecutive_good >= BPT1_EXIT_THRESHOLD:
|
||||
switch to Block, reset counter
|
||||
read(1) fail → mark NonTrimmed, consecutive_good = 0
|
||||
```
|
||||
|
||||
`BPT1_EXIT_THRESHOLD = 10_000` sectors (= 20 MB of clean data).
|
||||
Calibrated from the 2026-04-26 BU40N empirical run; tunable.
|
||||
|
||||
Per-block cost on a 60-sector damaged block with 1 bad sector:
|
||||
- Bisect (v0.13.21): ~30 sec (5 s × 6 levels)
|
||||
- Hysteresis (v0.13.22): ~10 sec (5 s bpt=batch fail + 59 × 1 ms good
|
||||
+ 1 × 5 s bad)
|
||||
|
||||
Plus inside a damaged cluster spanning many 60-blocks, hysteresis
|
||||
pays the bpt=batch fail cost ONCE on entry; bisection paid it every
|
||||
60 sectors. For Dune 2's ~1248-sector boundary cluster that's ~21
|
||||
fewer 5-sec waits = ~100 sec saved.
|
||||
|
||||
Telemetry: new `phase=mode_change` trace event with `from`, `to`,
|
||||
`lba`, `consecutive_good`. Replaces v0.13.21's `phase=bisect`. The
|
||||
v0.13.21 worklist DFS is gone — single iterative `for s in 0..count`
|
||||
on the failure path.
|
||||
|
||||
Test rename:
|
||||
`test_disc_copy_bisect_recovers_via_single_sector_reads` →
|
||||
`test_disc_copy_hysteresis_recovers_via_single_sector_reads`. Same
|
||||
synthetic BU40N-pattern reader; same 100% recovery expectation.
|
||||
|
||||
## 0.13.21 (2026-04-26)
|
||||
|
||||
### Fix: Disc::copy bisect-on-fail (replaces skip-forward)
|
||||
|
||||
Reference in New Issue
Block a user