Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ pub enum Error {
IOError(io::Error),
#[cfg(feature = "std")]
OsStringError(std::ffi::OsString),
ControlCommandRejected,
CrashLogPending,
SourceBusy,
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -74,6 +77,9 @@ impl fmt::Display for Error {
Error::IOError(err) => write!(f, "Encountered IO error: {err}"),
#[cfg(feature = "std")]
Error::OsStringError(s) => write!(f, "Cannot convert OS string: {s:?}"),
Error::ControlCommandRejected => write!(f, "Control command was rejected"),
Error::CrashLogPending => write!(f, "A Crash Log collection is already pending"),
Error::SourceBusy => write!(f, "Crash Log source is busy"),
}
}
}
Expand Down
59 changes: 43 additions & 16 deletions lib/src/source/pmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use alloc::{
use std::{collections::BTreeSet, fmt, str::FromStr};
#[cfg(all(target_os = "linux", feature = "std"))]
use sysfs::PmtSysFs;
#[cfg(all(target_os = "linux", feature = "control_commands"))]
use sysfs::PmtSysFsEndpoint;

pub use bdf::PciBdf;

Expand Down Expand Up @@ -87,10 +89,7 @@ impl Pmt {

#[cfg(all(target_os = "linux", feature = "control_commands"))]
pub fn enable_disable(&self, dev: &PmtDeviceId, enable: bool) -> Result<(), Error> {
for endpoint in self.sysfs.get_endpoints(dev) {
endpoint.enable_disable(enable)?;
}
Ok(())
self.run_on_endpoints(dev, |endpoint| endpoint.enable_disable(enable))
}

#[cfg(all(not(target_os = "linux"), feature = "control_commands"))]
Expand All @@ -100,10 +99,7 @@ impl Pmt {

#[cfg(all(target_os = "linux", feature = "control_commands"))]
pub fn clear(&self, dev: &PmtDeviceId) -> Result<(), Error> {
for endpoint in self.sysfs.get_endpoints(dev) {
endpoint.clear()?;
}
Ok(())
self.run_on_endpoints(dev, |endpoint| endpoint.clear())
}

#[cfg(all(not(target_os = "linux"), feature = "control_commands"))]
Expand All @@ -113,10 +109,7 @@ impl Pmt {

#[cfg(all(target_os = "linux", feature = "control_commands"))]
pub fn rearm(&self, dev: &PmtDeviceId) -> Result<(), Error> {
for endpoint in self.sysfs.get_endpoints(dev) {
endpoint.rearm()?;
}
Ok(())
self.run_on_endpoints(dev, |endpoint| endpoint.rearm())
}

#[cfg(all(not(target_os = "linux"), feature = "control_commands"))]
Expand All @@ -126,17 +119,51 @@ impl Pmt {

#[cfg(all(target_os = "linux", feature = "control_commands"))]
pub fn trigger(&self, dev: &PmtDeviceId) -> Result<(), Error> {
for endpoint in self.sysfs.get_endpoints(dev) {
endpoint.trigger()?;
}
Ok(())
self.run_on_endpoints(dev, |endpoint| endpoint.trigger())
}

#[cfg(all(not(target_os = "linux"), feature = "control_commands"))]
pub fn trigger(&self, _dev: &PmtDeviceId) -> Result<(), Error> {
Err(Error::Unsupported)
}

#[cfg(all(target_os = "linux", feature = "control_commands"))]
fn run_on_endpoints<F>(&self, dev: &PmtDeviceId, command: F) -> Result<(), Error>
where
F: Fn(&PmtSysFsEndpoint) -> Result<(), Error>,
{
let endpoints = self.sysfs.get_endpoints(dev);
if endpoints.is_empty() {
return Err(Error::NoCrashLogSourceFound);
}

let mut cmd_success = false;
let mut first_error = None;

for endpoint in &endpoints {
match command(endpoint) {
Ok(()) => cmd_success = true,
Err(err) => {
if first_error.is_none() {
first_error = Some(err);
}
}
}
}

if !cmd_success {
// Unreachable `unwrap_or`: `endpoints` is not empty and every failing endpoint
// records an error, so `first_error` is always set when no endpoint succeeded.
return Err(first_error.unwrap_or(Error::InternalError));
}

if let Some(err) = first_error {
log::warn!("Crash Log command failed on some endpoints: {err}");
}

Ok(())
}

pub fn description(&self, dev: &PmtDeviceId) -> String {
match dev {
PmtDeviceId::Name(name) => format!("PMT endpoint ({name})"),
Expand Down
17 changes: 14 additions & 3 deletions lib/src/source/pmt/sysfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,26 @@ impl PmtSysFsEndpoint {
.truncate(true)
.open(&path)
.map_err(Error::IOError)
.inspect_err(|err| log::warn!("{}: {err}", path.display()))?;
.inspect_err(|err| log::info!("Failed to open {}: {err}", path.display()))?;

file.write_all(value)
.map_err(Error::IOError)
.inspect_err(|err| log::warn!("Failed to write to {}: {err}", path.display()))?;
.map_err(Self::to_control_error)
.inspect_err(|err| log::info!("Failed to write to {}: {err}", path.display()))?;

Ok(())
}

#[cfg(feature = "control_commands")]
fn to_control_error(err: std::io::Error) -> Error {
log::debug!("Error reported by the PmtSysFs: {err}");
match err.kind() {
std::io::ErrorKind::AlreadyExists => Error::CrashLogPending,
std::io::ErrorKind::ResourceBusy => Error::SourceBusy,
std::io::ErrorKind::InvalidInput => Error::ControlCommandRejected,
_ => Error::IOError(err),
}
}

pub fn capabilities(&self) -> Capabilities {
let mut capabilities: Capabilities = Capabilities::new();

Expand Down
Loading