error: treat IoError/DeviceNotFound as SCSI transport failures (dead bus)
is_scsi_transport_failure recognized only the 0xFF SCSI sentinel, so the two NON-SCSI dead-bus faults the Linux SG_IO transport returns — Error::IoError (ioctl(SG_IO) == -1: ENODEV/EIO on an unplugged bridge) and Error::DeviceNotFound (fd gone) — were classified as recoverable bad sectors. Every caller (sweep read_error, patch, single-pass fill_extents) would then zero-fill / skip-ahead across a wedged bus instead of aborting the pass and re-enumerating the bridge — potentially zero-filling a huge span. Add both to the classifier so a dead bus aborts the pass everywhere. (Companion to the unlock_bridge fix, which maps the same two faults to 0xFF across the freemkv-unlock seam.)
This commit is contained in:
+31
-5
@@ -879,6 +879,15 @@ impl Error {
|
|||||||
status: Some(crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE),
|
status: Some(crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
) || matches!(
|
||||||
|
// A failed `ioctl(SG_IO)` (Error::IoError, e.g. ENODEV/EIO on an
|
||||||
|
// unplugged USB bridge) and a vanished device (Error::DeviceNotFound,
|
||||||
|
// fd gone) are dead-bus / transport-layer faults too — NOT recoverable
|
||||||
|
// bad sectors. Treat them as transport failures so sweep / patch /
|
||||||
|
// fill_extents abort the pass and re-enumerate the bridge instead of
|
||||||
|
// zero-filling every read against a wedged device.
|
||||||
|
self,
|
||||||
|
Error::IoError { .. } | Error::DeviceNotFound { .. }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,11 +1384,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// is_scsi_transport_failure returns true only for SCSI_STATUS_TRANSPORT_FAILURE (0xFF).
|
/// is_scsi_transport_failure is true for the 0xFF SCSI sentinel AND for the
|
||||||
/// Spec: comment on SCSI_STATUS_TRANSPORT_FAILURE says "synthesised sentinel: the
|
/// non-SCSI dead-bus faults (Error::IoError from a failed ioctl(SG_IO),
|
||||||
/// transport never delivered a SCSI status byte".
|
/// Error::DeviceNotFound from a vanished fd) — but NEVER for a real SCSI
|
||||||
|
/// reply (CHECK CONDITION) or unrelated errors.
|
||||||
/// Mutation: testing against 0x02 (CHECK CONDITION) would wrongly mark CHECK
|
/// Mutation: testing against 0x02 (CHECK CONDITION) would wrongly mark CHECK
|
||||||
/// CONDITION replies as transport failures.
|
/// CONDITION replies as transport failures; dropping the IoError/
|
||||||
|
/// DeviceNotFound arm would let a dead bus zero-fill the disc.
|
||||||
#[test]
|
#[test]
|
||||||
fn is_scsi_transport_failure_only_for_0xff() {
|
fn is_scsi_transport_failure_only_for_0xff() {
|
||||||
use crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE;
|
use crate::scsi::SCSI_STATUS_TRANSPORT_FAILURE;
|
||||||
@@ -1403,7 +1414,22 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert!(!cc.is_scsi_transport_failure());
|
assert!(!cc.is_scsi_transport_failure());
|
||||||
|
|
||||||
// False for non-SCSI errors.
|
// True: non-SCSI dead-bus faults — a failed ioctl(SG_IO) and a vanished
|
||||||
|
// device are transport-layer failures, not recoverable bad sectors.
|
||||||
|
assert!(
|
||||||
|
Error::IoError {
|
||||||
|
source: std::io::Error::from(std::io::ErrorKind::NotConnected)
|
||||||
|
}
|
||||||
|
.is_scsi_transport_failure()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
Error::DeviceNotFound {
|
||||||
|
path: "/dev/sg9".into()
|
||||||
|
}
|
||||||
|
.is_scsi_transport_failure()
|
||||||
|
);
|
||||||
|
|
||||||
|
// False for unrelated errors.
|
||||||
assert!(!Error::Halted.is_scsi_transport_failure());
|
assert!(!Error::Halted.is_scsi_transport_failure());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user