v0.13.20 — sync blocking SG_IO + cross-platform parity strip

- scsi/linux.rs: full rewrite from async write/poll/read+1.5s timeout+
  close-on-timeout to one synchronous ioctl(fd, SG_IO, &hdr). Kernel
  honors hdr.timeout and runs its own ABORT/RESET escalation. Errors
  check host_status and driver_status (both 0xFF-synthesised) plus
  status. Sense-key parser handles descriptor (0x72/0x73) + fixed
  (0x70/0x71) formats. Deleted fd_recovery, bg close+open thread, fd
  swap dance. -331/+155 lines.

- scsi/macos.rs: try_recover() removed (userspace handle-recovery on
  task failure was the same anti-pattern stripped from Linux). bsd_name
  field deleted. Errors bubble up directly.

- scsi/windows.rs: try_recover() removed, wide_path field deleted,
  INVALID_HANDLE guard removed.

- scsi/mod.rs: parse_sense_key() helper extracted (used by all three
  platforms now — single canonical sense-key parse rather than three
  inlined copies). +10 unit tests covering descriptor format, fixed
  format, truncated buffers, unknown response codes.

- drive/mod.rs: Drive::reset() deleted (escalating eject + STOP/START +
  reinit recovery — per audit, kernel handles its own escalation;
  userspace shouldn't).
  pub fn find_drives() -> Vec<Drive> deleted (opened N drives just to
  throw most away). find_drive() now uses discover_drives() directly.
  wait_ready() simplified — drops the reset path on sense_key=5,
  just keeps polling TUR for 60 iterations.

- lib.rs: find_drives re-export removed.

- benches/sgio_read.rs: switched to find_drive() (no longer iterates a
  drive list).

Net: 9 files changed, 226 insertions(+), 473 deletions(-). 329 tests
pass, clippy -D warnings clean. No consumer breakage (CLI, autorip,
bdemu compile + test green).

Architecture decision documented in
(internal)/docs/audits/2026-04-26-scsi-architecture-research.md
(primary-source survey of MakeMKV, sg_dd, ddrescue, and the kernel
mid-layer's own scsi_eh.rst escalation ladder).
This commit is contained in:
MattJackson
2026-04-26 09:51:46 -07:00
parent b4de5d343d
commit d2905ba7bb
9 changed files with 332 additions and 473 deletions
+63
View File
@@ -1,5 +1,68 @@
# Changelog
## 0.13.20 (2026-04-26)
### Architecture: SCSI transport — sync blocking SG_IO
`scsi/linux.rs` rewritten from async `write/poll/read + 1.5 s timeout
+ close-on-timeout in bg thread` to a single synchronous blocking
`ioctl(fd, SG_IO, &hdr)`. The old pattern abandoned slow-but-alive
commands faster than the drive could drain its internal queue,
deepening the BU40N wedge. Per the audit at
`(internal)/docs/audits/2026-04-26-scsi-architecture-research.md`,
no reference project (MakeMKV / sg_dd / ddrescue) does what we did —
all use sync blocking SG_IO with 8-60 s timeouts and let the kernel's
mid-layer (`scsi_eh.rst`) run ABORT TASK / LUN RESET / BUS RESET /
HOST RESET escalation internally.
What changed:
- `SgIoTransport::execute()` is one syscall now. Caller-supplied
`timeout_ms` is honored by the kernel, which does its own
ABORT/RESET escalation if the device times out.
- Errors check `host_status` and `driver_status` (both 0xFF-synthesised
for the caller) in addition to `status` — transport-level failures
no longer slip through as Ok.
- Sense-key parser handles both descriptor format (0x72/0x73, key at
byte 1) and fixed format (0x70/0x71, key at byte 2).
- Deleted the `fd_recovery: Arc<AtomicI32>` field, the bg close+open
thread, and the stale-fd swap dance. `scsi/linux.rs` shrank from
~720 to ~520 lines.
- Module doc rewritten to reflect the new architecture.
### Architecture: parity strip on macOS + Windows
`scsi/macos.rs` and `scsi/windows.rs` had `try_recover()`
userspace handle-recovery on task failure. Same anti-pattern as the
Linux fd-recovery dance, removed for the same reason: the kernel
mid-layer already runs its own escalation. Errors bubble up directly.
Cleanups:
- `MacScsiTransport`: `try_recover()` deleted, `bsd_name` field
deleted (was only used by try_recover), fail-fast device_iface guard
deleted (no longer null'd mid-session).
- `SptiTransport`: `try_recover()` deleted, `wide_path` field deleted,
INVALID_HANDLE guard deleted.
### API cleanup: drop `Drive::reset` and `find_drives`
Two duplicates removed from the public surface:
- `Drive::reset()` — escalating recovery (STOP/START unit + eject +
reinit). Per the audit, userspace shouldn't escalate; the kernel
already does. Only one internal caller (`wait_ready` line 195),
which now just keeps polling TUR for 60 iterations. No external
consumer used it.
- `pub fn find_drives() -> Vec<Drive>` — opened N drives just to throw
most away. Only caller was `find_drive()` itself, which now uses
`discover_drives()` directly. No external consumer used it. For
lightweight enumeration (UI sidebar etc.) use `scsi::list_drives()`.
`lib.rs` re-export of `find_drives` removed.
## 0.13.19 (2026-04-26 — held, never released)
Held in development; folded into 0.13.20.
## 0.13.18 (2026-04-26)
### Sync release — no functional changes