diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 27043dc8778d75..56f5855e975075 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -6,6 +6,7 @@ use crate::{bindings, c_types}; use alloc::{alloc::AllocError, collections::TryReserveError}; +use core::convert::From; use core::{num::TryFromIntError, str::Utf8Error}; /// Generic integer kernel error. @@ -104,3 +105,50 @@ impl From for Error { Error::ENOMEM } } + +// # Invariant: `-bindings::MAX_ERRNO` fits in an `i16`. +crate::static_assert!(bindings::MAX_ERRNO <= -(i16::MIN as i32) as u32); + +#[doc(hidden)] +pub fn from_kernel_result_helper(r: Result) -> T +where + T: From, +{ + match r { + Ok(v) => v, + // NO-OVERFLOW: negative `errno`s are no smaller than `-bindings::MAX_ERRNO`, + // `-bindings::MAX_ERRNO` fits in an `i16` as per invariant above, + // therefore a negative `errno` always fits in an `i16` and will not overflow. + Err(e) => T::from(e.to_kernel_errno() as i16), + } +} + +/// Transforms a [`crate::error::Result`] to a kernel C integer result. +/// +/// This is useful when calling Rust functions that return [`crate::error::Result`] +/// from inside `extern "C"` functions that need to return an integer +/// error result. +/// +/// `T` should be convertible to an `i16` via `From`. +/// +/// # Examples +/// +/// ```rust,no_run +/// unsafe extern "C" fn probe_callback( +/// pdev: *mut bindings::platform_device, +/// ) -> c_types::c_int { +/// from_kernel_result! { +/// let ptr = devm_alloc(pdev)?; +/// rust_helper_platform_set_drvdata(pdev, ptr); +/// Ok(0) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! from_kernel_result { + ($($tt:tt)*) => {{ + $crate::error::from_kernel_result_helper((|| { + $($tt)* + })()) + }}; +} diff --git a/rust/kernel/file_operations.rs b/rust/kernel/file_operations.rs index cf59acbcd500ae..3c15bf387438aa 100644 --- a/rust/kernel/file_operations.rs +++ b/rust/kernel/file_operations.rs @@ -13,6 +13,7 @@ use crate::{ bindings, c_types, error::{Error, Result}, file::File, + from_kernel_result, io_buffer::{IoBufferReader, IoBufferWriter}, iov_iter::IovIter, sync::CondVar, @@ -78,25 +79,6 @@ pub enum SeekFrom { Current(i64), } -fn from_kernel_result(r: Result) -> T -where - T: TryFrom, - T::Error: core::fmt::Debug, -{ - match r { - Ok(v) => v, - Err(e) => T::try_from(e.to_kernel_errno()).unwrap(), - } -} - -macro_rules! from_kernel_result { - ($($tt:tt)*) => {{ - from_kernel_result((|| { - $($tt)* - })()) - }}; -} - unsafe extern "C" fn open_callback>( inode: *mut bindings::inode, file: *mut bindings::file,