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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user