driver-manager: F6b — AER 6-state mapping (Linux pci_ers_result parity)

Closes the C13 gap: 4-state -> 6-state.

Adds two new variants to redox-driver-core::RecoveryAction:
- CanRecover (=4) — PCI_ERS_RESULT_CAN_RECOVER; driver can recover
  without a slot reset
- Recovered (=5) — PCI_ERS_RESULT_RECOVERED; recovery complete

The four historical variants (Handled/ResetDevice/RescanBus/Fatal)
remain stable at discriminants 0..=3; the wire protocol is backward
compatible.

Cross-crate surfaces updated:
- linux-kpi c_headers/linux/pci.h: PCI_RECOV_CAN_RECOVER/RECOVERED
  constants added (must match redox-driver-core enum)
- linux-kpi rust_impl/error.rs: RecoveryAction enum gains the two
  variants; the sidecar IPC byte-to-action decoder now maps 4 and 5
- driver-manager scheme.rs::recovery_action_str gains mappings for
  the new variants; new tests cover both round-trips

Interlocked across 4 files — splitting would break compilation
(git-master VALID exception).

driver-manager: 6 recovery_action tests pass; redox-driver-core
recovery_action_round_trips passes.
This commit is contained in:
2026-07-27 12:21:10 +09:00
parent 37e5107510
commit d233190d68
4 changed files with 52 additions and 9 deletions
@@ -122,10 +122,12 @@ extern int pci_restore_state(struct pci_dev *dev);
#define PCI_ERR_FATAL 2
/* Action discriminants (must match redox-driver-core::RecoveryAction). */
#define PCI_RECOV_HANDLED 0
#define PCI_RECOV_RESET 1
#define PCI_RECOV_RESCAN_BUS 2
#define PCI_RECOV_FATAL 3
#define PCI_RECOV_HANDLED 0
#define PCI_RECOV_RESET 1
#define PCI_RECOV_RESCAN_BUS 2
#define PCI_RECOV_FATAL 3
#define PCI_RECOV_CAN_RECOVER 4
#define PCI_RECOV_RECOVERED 5
/*
* Register a C error handler and start the worker thread that
@@ -29,6 +29,8 @@ enum RecoveryAction {
ResetDevice = 1,
RescanBus = 2,
Fatal = 3,
CanRecover = 4,
Recovered = 5,
}
/// Wire payload sent from driver-manager to the spawned driver.
@@ -143,6 +145,8 @@ fn worker_loop(fd: i32) {
0 => RecoveryAction::Handled,
1 => RecoveryAction::ResetDevice,
2 => RecoveryAction::RescanBus,
4 => RecoveryAction::CanRecover,
5 => RecoveryAction::Recovered,
_ => RecoveryAction::Fatal,
};
let response = DriverErrorResponse(action).encode();
@@ -49,16 +49,31 @@ pub enum ErrorSeverity {
}
/// Action the manager should take in response to a `Driver::on_error` callback.
///
/// Mirrors Linux's `pci_ers_result` (include/linux/pci.h). The four historical
/// variants (`Handled`/`ResetDevice`/`RescanBus`/`Fatal`) are stable and
/// occupy discriminants 0..=3 on the wire. The two newer variants (`CanRecover`
/// and `Recovered`) extend the set to match Linux's 6-state model and occupy
/// discriminants 4 and 5; both must be handled by any consumer that parses
/// the sidecar IPC payload.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RecoveryAction {
/// Driver handled the error; continue normally.
/// Driver handled the error; continue normally. Maps to `PCI_ERS_RESULT_NONE`.
Handled,
/// Driver requests a reset of the device via pcid.
/// Driver requests a reset of the device via pcid. Maps to `PCI_ERS_RESULT_NEED_RESET`.
ResetDevice,
/// Driver requests the entire slot to be rescanned.
/// Driver requests the entire slot to be rescanned. Maps to a hard rescan, not a
/// specific `pci_ers_result` value; used by `route_to_driver` when the bound
/// driver does not respond on the sidecar IPC and the severity is Fatal.
RescanBus,
/// Driver failed to recover; escalate to the operator.
/// Driver failed to recover; escalate to the operator. Maps to `PCI_ERS_RESULT_DISCONNECT`.
Fatal,
/// Driver can recover without a slot reset. Maps to `PCI_ERS_RESULT_CAN_RECOVER`.
/// The manager clears the AER status registers via pcid and lets I/O resume.
CanRecover,
/// Recovery completed successfully. Maps to `PCI_ERS_RESULT_RECOVERED`.
/// The manager acknowledges completion and resumes normal scheduling.
Recovered,
}
/// The 6-state PCI error-recovery result, mirroring Linux's
@@ -414,7 +414,11 @@ impl DriverManagerScheme {
/// Map a `RecoveryAction` (from `Driver::on_error` / AER routing)
/// to the action vocabulary accepted by `dispatch_recovery`.
/// Returns `None` for actions that don't need an auto-dispatch
/// (e.g. `Handled`, `Fatal` which is operator-only).
/// (e.g. `Handled`, `Fatal` which is operator-only). `CanRecover`
/// and `Recovered` map to informational strings so they show up in
/// the AER dispatch log but do not trigger a separate auto-dispatch
/// — `CanRecover` clears AER status via pcid, and `Recovered` is a
/// terminal acknowledgement.
#[cfg(any(test, target_os = "redox"))]
pub fn recovery_action_str(action: RecoveryAction) -> Option<&'static str> {
match action {
@@ -422,6 +426,8 @@ impl DriverManagerScheme {
RecoveryAction::ResetDevice => Some("reset_device"),
RecoveryAction::RescanBus => Some("rescan_bus"),
RecoveryAction::Fatal => None,
RecoveryAction::CanRecover => Some("can_recover"),
RecoveryAction::Recovered => Some("recovered"),
}
}
@@ -859,6 +865,22 @@ mod tests {
);
}
#[test]
fn recovery_action_can_recover_maps_to_can_recover_str() {
assert_eq!(
DriverManagerScheme::recovery_action_str(RecoveryAction::CanRecover),
Some("can_recover")
);
}
#[test]
fn recovery_action_recovered_maps_to_recovered_str() {
assert_eq!(
DriverManagerScheme::recovery_action_str(RecoveryAction::Recovered),
Some("recovered")
);
}
#[test]
fn new_id_parses_vendor_device() {
let (driver, entry) = parse_new_id("e1000d 0x8086 0x100E").unwrap();