From: Filipe Xavier felipe_life@live.com
commit e9759c5b9ea555d09f426c70c880e9522e9b0576 upstream.
Optimize `Result<(), Error>` size by changing `Error` type to `NonZero*` for niche optimization.
This reduces the space used by the `Result` type, as the `NonZero*` type enables the compiler to apply more efficient memory layout. For example, the `Result<(), Error>` changes size from 8 to 4 bytes.
Link: https://github.com/Rust-for-Linux/linux/issues/1120 Signed-off-by: Filipe Xavier felipe_life@live.com Reviewed-by: Gary Guo gary@garyguo.net Reviewed-by: Alice Ryhl aliceryhl@google.com Reviewed-by: Fiona Behrens me@kloenk.dev Link: https://lore.kernel.org/r/BL0PR02MB4914B9B088865CF237731207E9732@BL0PR02MB49... [ Removed unneeded block around `match`, added backticks in panic message and added intra-doc link. - Miguel ] Signed-off-by: Miguel Ojeda ojeda@kernel.org --- rust/kernel/error.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 2f1e4b783bfb..be6509d5f4a4 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -9,6 +9,7 @@ use alloc::alloc::LayoutError;
use core::fmt; +use core::num::NonZeroI32; use core::num::TryFromIntError; use core::str::Utf8Error;
@@ -20,7 +21,11 @@ macro_rules! declare_err { $( #[doc = $doc] )* - pub const $err: super::Error = super::Error(-(crate::bindings::$err as i32)); + pub const $err: super::Error = + match super::Error::try_from_errno(-(crate::bindings::$err as i32)) { + Some(err) => err, + None => panic!("Invalid errno in `declare_err!`"), + }; }; }
@@ -88,7 +93,7 @@ macro_rules! declare_err { /// /// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`). #[derive(Clone, Copy, PartialEq, Eq)] -pub struct Error(core::ffi::c_int); +pub struct Error(NonZeroI32);
impl Error { /// Creates an [`Error`] from a kernel error code. @@ -107,7 +112,20 @@ pub fn from_errno(errno: core::ffi::c_int) -> Error {
// INVARIANT: The check above ensures the type invariant // will hold. - Error(errno) + // SAFETY: `errno` is checked above to be in a valid range. + unsafe { Error::from_errno_unchecked(errno) } + } + + /// Creates an [`Error`] from a kernel error code. + /// + /// Returns [`None`] if `errno` is out-of-range. + const fn try_from_errno(errno: core::ffi::c_int) -> Option<Error> { + if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 { + return None; + } + + // SAFETY: `errno` is checked above to be in a valid range. + Some(unsafe { Error::from_errno_unchecked(errno) }) }
/// Creates an [`Error`] from a kernel error code. @@ -115,21 +133,22 @@ pub fn from_errno(errno: core::ffi::c_int) -> Error { /// # Safety /// /// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`). - unsafe fn from_errno_unchecked(errno: core::ffi::c_int) -> Error { + const unsafe fn from_errno_unchecked(errno: core::ffi::c_int) -> Error { // INVARIANT: The contract ensures the type invariant // will hold. - Error(errno) + // SAFETY: The caller guarantees `errno` is non-zero. + Error(unsafe { NonZeroI32::new_unchecked(errno) }) }
/// Returns the kernel error code. pub fn to_errno(self) -> core::ffi::c_int { - self.0 + self.0.get() }
#[cfg(CONFIG_BLOCK)] pub(crate) 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) } + unsafe { bindings::errno_to_blk_status(self.0.get()) } }
/// Returns the error encoded as a pointer. @@ -137,7 +156,7 @@ pub fn to_ptr<T>(self) -> *mut T { #[cfg_attr(target_pointer_width = "32", allow(clippy::useless_conversion))] // SAFETY: `self.0` is a valid error due to its invariant. unsafe { - bindings::ERR_PTR(self.0.into()) as *mut _ + bindings::ERR_PTR(self.0.get().into()) as *mut _ } }
@@ -145,7 +164,7 @@ pub fn to_ptr<T>(self) -> *mut T { #[cfg(not(testlib))] pub fn name(&self) -> Option<&'static CStr> { // SAFETY: Just an FFI call, there are no extra safety requirements. - let ptr = unsafe { bindings::errname(-self.0) }; + let ptr = unsafe { bindings::errname(-self.0.get()) }; if ptr.is_null() { None } else {