disc: wedge AVOIDANCE on Pass 1 — inter-error pause + larger jumps
Complements the wedge-skip backstop (d7f1862) with proactive avoidance so we don't HIT the wedge in the first place. User's take after seeing the Dune Pt 2 rip wedge at 48%: 'we shouldn't be wedging.' Empirical observations from the 23:09:12-23:09:55 wedge timeline: 5 read errors over 43 s, ~8 s apart (drive's own ECC recovery takes 5-10 s per failure). Not 'hammering' in any usual sense, but cumulative firmware-state buildup over 5 in-cluster errors was enough to tip the BU40N into wedge mode at the 5th error. Damage cluster spanned ~140 MB (LBAs 19.898M-19.965M). Current damage-jump base of 256 sectors × batch=32 = 16 MB first jump, doubling to 32 MB, 64 MB... Each jump landed BACK INSIDE the 140 MB cluster, exposing the drive to MORE in-cluster errors. Two avoidance levers: 1. Inter-error pause on Pass 1 (PASS_1_FAIL_PAUSE_SECS = 5 s): pre-fix Pass 1 ran pause_secs=0 on all errors to 'zoom past' damage zones. Successful reads still zoom at zero pause — the pause applies only to FAILED reads, giving the drive's firmware cool-down between cluster exposures. Cost: ~5 s per scattered failure (~30-60 s total on a damage cluster); trivial vs. crashing the rip at 48%. 2. Larger damage-jump base (JUMP_BASE_SECTORS = 1024, up from 256): first jump at batch=32 now covers 64 MB instead of 16 MB, second jump 128 MB instead of 32 MB. Two jumps clear 192 MB — well past most single-cluster damage patterns. Smaller jumps were landing inside the cluster and adding to the wedge counter. Plus a halt-aware sleep helper (sleep_secs_or_halt) so the new inter-error pause doesn't degrade halt response time. Halt poll granularity 100 ms — halt fires within ~100 ms regardless of remaining pause time. Updated three sleep call sites in disc/mod.rs (SkipBlock pause, JumpAhead post-pause, Retry pause). The wedge-SKIP backstop (d7f1862) stays — combined with this avoidance work, the flow becomes: damage cluster encountered → pause 5 s, mark NonTrimmed → second failure → pause 5 s, mark NonTrimmed → ... threshold hit → damage-jump 64 MB (clears 95% of clusters) → if jump lands in another cluster: 128 MB next jump → only if drive STILL wedges after all this: wedge-skip kicks in (1 GB jump + 30 s cooldown × 16 budget) Tests: pass_1_pauses_briefly_on_skip_for_wedge_avoidance — locks the new 5 s pause behavior in place (replaces the old pause=0 test). integration test threshold bumped from 5 s to 60 s with comment explaining the new bound is 'not infinite' rather than 'milliseconds-fast'. All 433+ tests green on cargo +1.86 fmt + clippy + test. Precommit green.
This commit is contained in:
@@ -459,11 +459,17 @@ fn test_disc_copy_completes_full_disc_with_failing_reader() {
|
||||
let _ = std::fs::remove_file(&iso_path);
|
||||
let _ = std::fs::remove_file(libfreemkv::disc::mapfile_path_for(&iso_path));
|
||||
|
||||
// Hard bound — even at 0 ms per read, 1024 sectors with skip-forward
|
||||
// should complete in well under a second on any host.
|
||||
// Hard bound — Pass 1 must NOT infinite-loop on a fully-failing
|
||||
// reader. The threshold accommodates the 2026-05-10 wedge-
|
||||
// avoidance pause (PASS_1_FAIL_PAUSE_SECS = 5 s on each failed
|
||||
// batch). With batch=32 and 1024 sectors that's up to ~5 batch
|
||||
// failures + a few damage-jump pauses before fast-trigger jumps
|
||||
// us past end-of-disc — well-bounded total, ~20-30 s typical.
|
||||
// The point of this test is "finishes cleanly, not infinitely",
|
||||
// not "completes in milliseconds."
|
||||
assert!(
|
||||
elapsed < Duration::from_secs(5),
|
||||
"Pass 1 took {elapsed:?} on a 2 MB synthetic disc — expected < 5 s"
|
||||
elapsed < Duration::from_secs(60),
|
||||
"Pass 1 took {elapsed:?} on a 2 MB synthetic disc — expected < 60 s (not infinite)"
|
||||
);
|
||||
|
||||
// Per RIP_DESIGN.md §2.1: Pass 1 must reach end of disc regardless of
|
||||
|
||||
Reference in New Issue
Block a user