Block layer status codes, represented by `blk_status_t`, are only one byte. This is different from the general kernel error codes. Add `BlkError` and `BlkResult` to handle these status codes. Signed-off-by: Andreas Hindborg --- rust/kernel/block.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/error.rs | 3 +- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 19236ab95227b..6cd83dc75a5eb 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -18,3 +18,97 @@ /// The difference between the size of a page and the size of a sector, /// expressed as a power of two. pub const PAGE_SECTORS_SHIFT: u32 = bindings::PAGE_SECTORS_SHIFT; + +pub mod error { + //! Block layer errors. + + use core::num::NonZeroU8; + + pub mod code { + //! C compatible error codes for the block subsystem. + macro_rules! declare_err { + ($err:tt $(,)? $($doc:expr),+) => { + $( + #[doc = $doc] + )* + pub const $err: super::BlkError = + match super::BlkError::try_from_blk_status(crate::bindings::$err as u8) { + Some(err) => err, + None => panic!("Invalid errno in `declare_err!`"), + }; + }; + } + + declare_err!(BLK_STS_NOTSUPP, "Operation not supported."); + declare_err!(BLK_STS_IOERR, "Generic IO error."); + declare_err!(BLK_STS_DEV_RESOURCE, "Device resource busy. Retry later."); + } + + /// A wrapper around a 1 byte block layer error code. + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct BlkError(NonZeroU8); + + impl BlkError { + /// Create a [`BlkError`] from a `blk_status_t`. + /// + /// If the code is not know, this function will warn and return [`code::BLK_STS_IOERR`]. + pub fn from_blk_status(status: bindings::blk_status_t) -> Self { + if let Some(error) = Self::try_from_blk_status(status) { + error + } else { + kernel::pr_warn!("Attempted to create `BlkError` from invalid value"); + code::BLK_STS_IOERR + } + } + + /// Convert `Self` to the underlying type. + pub fn to_blk_status(self) -> bindings::blk_status_t { + self.0.into() + } + + /// Try to create a `Self` form a `blk_status_t`. + /// + /// Returns `None` if the conversion fails. + const fn try_from_blk_status(errno: bindings::blk_status_t) -> Option { + if errno == 0 { + None + } else { + Some(BlkError( + // SAFETY: We just checked that `errno`is nonzero. + unsafe { NonZeroU8::new_unchecked(errno) }, + )) + } + } + } + + impl From for u8 { + fn from(value: BlkError) -> Self { + value.0.into() + } + } + + impl From for u32 { + fn from(value: BlkError) -> Self { + let value: u8 = value.0.into(); + value.into() + } + } + + impl From for BlkError { + fn from(_value: kernel::error::Error) -> Self { + code::BLK_STS_IOERR + } + } + + /// A result with a [`BlkError`] error type. + pub type BlkResult = Result; + + /// Convert a `blk_status_t` to a `BlkResult`. + pub fn to_result(status: bindings::blk_status_t) -> BlkResult { + if status == bindings::BLK_STS_OK { + Ok(()) + } else { + Err(BlkError::from_blk_status(status)) + } + } +} diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 258b12afdcba3..e656465010d6c 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -162,8 +162,9 @@ pub fn to_errno(self) -> crate::ffi::c_int { self.0.get() } + /// Convert a generic kernel error to a block layer error. #[cfg(CONFIG_BLOCK)] - pub(crate) fn to_blk_status(self) -> bindings::blk_status_t { + pub fn to_blk_status(self) -> bindings::blk_status_t { // SAFETY: `self.0` is a valid error due to its invariant. unsafe { bindings::errno_to_blk_status(self.0.get()) } } -- 2.51.2