v0.17.5: Pass N kernel block-device fallback + per-range fixes
Direct-SATA BU40N + Dune Part Two UHD live testing exposed that the v0.17.3 single-shot SCSI READ path matched 0/22 of the small bad- sector LBAs that dd if=/dev/sr0 recovers on the same drive. This release closes that gap and fixes adjacent bugs silently capping recovery. - /dev/sr0 pread fallback in Drive::read (Linux only): on SCSI READ Err, fall back to posix_fadvise(DONTNEED) + pread() against the corresponding block device. Kernel sr_mod runs ~5 internal retries with no per-attempt mid-layer escalation overhead — the mechanism behind dd's recovery advantage. End-to-end byte verification confirms the fallback path returns real disc data. - Disc::patch per-range watchdog fix: MAX_RANGE_SECS was breaking 'outer (one slow range killed the entire patch). Now skips to the next range. Pre-fix patch died after 4 sectors of range 1 of 47. - Per-sector range budget: range_budget = sectors × 25 s, capped at 1800 s. Replaces the flat 180 s/range that was unfair to medium ranges and pointlessly generous to single-sector ones. - consecutive_failures resets per range. The wedge-exit detector is for stuck-on-one-range, not many-small-ranges-with-one-fail-each. - Reverted inline 5× retry experiment (was hurting: each retry paid kernel SCSI escalation overhead). Restored READ_RECOVERY_TIMEOUT_MS to 60 s. The kernel-auto-retry pattern is now provided by sr0 fallback. Empirical: pass 1 recovered 94.6 MB / 11 s of main title (33 sr0 saves). Pass 2 added 0.6 MB. Remaining ~233 MB on the test disc appears physically unrecoverable on this hardware.
This commit is contained in:
+19
-4
@@ -68,10 +68,25 @@ pub(crate) const TUR_TIMEOUT_MS: u32 = 5_000;
|
||||
pub(crate) const READ_TIMEOUT_MS: u32 = 10_000;
|
||||
|
||||
/// Timeout for content READ commands on the recovery path —
|
||||
/// [`disc::Disc::patch`]'s targeted retries on bad ranges. Doubles
|
||||
/// the fast-path budget so a sector that fails at 30 s gets one more
|
||||
/// honest attempt. Matches sg_dd's default per-command timeout
|
||||
/// (`DEF_TIMEOUT = 60000`).
|
||||
/// [`disc::Disc::patch`]'s targeted retries on bad ranges. Matches
|
||||
/// `sg_dd`'s 60 s ceiling: long enough that any sector the drive can
|
||||
/// recover at all gets the time to do so, short enough that an
|
||||
/// unresponsive bus is detected before the per-range watchdog fires.
|
||||
///
|
||||
/// In practice failed reads return in 1–4 s (the drive itself gives up
|
||||
/// on uncorrectable ECC before the timeout); the 60 s value is a
|
||||
/// safety ceiling, not a steady-state cost.
|
||||
///
|
||||
/// Historical note (2026-05-08): briefly lowered to 2 s with a 5×
|
||||
/// inline retry loop in `Disc::patch` to mimic the kernel `sr_mod`
|
||||
/// driver's auto-retry pattern. The synthetic logic worked but on the
|
||||
/// live drive each "2 s" read paid ~1.5 s of kernel SCSI mid-layer
|
||||
/// error escalation on top, so 5× retries took ~17 s per LBA and
|
||||
/// triggered MAX_RANGE_SECS after 4 sectors — pushing recovery to
|
||||
/// 0/22 ranges (worse than the 0/22 baseline of v0.17.3 single-shot
|
||||
/// at 60 s, since that at least visited every range). Reverted; the
|
||||
/// kernel-auto-retry approach is being pursued via a `/dev/sr0` pread
|
||||
/// fallback instead.
|
||||
pub(crate) const READ_RECOVERY_TIMEOUT_MS: u32 = 60_000;
|
||||
|
||||
// ── SCSI status bytes (SPC-4 §4.5.5) ────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user