| From stable+bounces-121489-greg=kroah.com@vger.kernel.org Fri Mar 7 23:52:14 2025 |
| From: Miguel Ojeda <ojeda@kernel.org> |
| Date: Fri, 7 Mar 2025 23:49:41 +0100 |
| Subject: rust: alloc: introduce `ArrayLayout` |
| To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>, stable@vger.kernel.org |
| Cc: Danilo Krummrich <dakr@kernel.org>, Alice Ryhl <aliceryhl@google.com>, Alyssa Ross <hi@alyssa.is>, NoisyCoil <noisycoil@disroot.org>, patches@lists.linux.dev, Miguel Ojeda <ojeda@kernel.org> |
| Message-ID: <20250307225008.779961-35-ojeda@kernel.org> |
| |
| From: Benno Lossin <benno.lossin@proton.me> |
| |
| commit 9e7bbfa182767f638ba61dba3518ff78da9f31ff upstream. |
| |
| When allocating memory for arrays using allocators, the `Layout::array` |
| function is typically used. It returns a result, since the given size |
| might be too big. However, `Vec` and its iterators store their allocated |
| capacity and thus they already did check that the size is not too big. |
| |
| The `ArrayLayout` type provides this exact behavior, as it can be |
| infallibly converted into a `Layout`. Instead of a `usize` capacity, |
| `Vec` and other similar array-storing types can use `ArrayLayout` |
| instead. |
| |
| Reviewed-by: Gary Guo <gary@garyguo.net> |
| Signed-off-by: Benno Lossin <benno.lossin@proton.me> |
| Signed-off-by: Danilo Krummrich <dakr@kernel.org> |
| Link: https://lore.kernel.org/r/20241004154149.93856-16-dakr@kernel.org |
| [ Formatted a few comments. - Miguel ] |
| Signed-off-by: Miguel Ojeda <ojeda@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| rust/kernel/alloc.rs | 1 |
| rust/kernel/alloc/layout.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 92 insertions(+) |
| create mode 100644 rust/kernel/alloc/layout.rs |
| |
| --- a/rust/kernel/alloc.rs |
| +++ b/rust/kernel/alloc.rs |
| @@ -5,6 +5,7 @@ |
| #[cfg(not(any(test, testlib)))] |
| pub mod allocator; |
| pub mod kbox; |
| +pub mod layout; |
| pub mod vec_ext; |
| |
| #[cfg(any(test, testlib))] |
| --- /dev/null |
| +++ b/rust/kernel/alloc/layout.rs |
| @@ -0,0 +1,91 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| + |
| +//! Memory layout. |
| +//! |
| +//! Custom layout types extending or improving [`Layout`]. |
| + |
| +use core::{alloc::Layout, marker::PhantomData}; |
| + |
| +/// Error when constructing an [`ArrayLayout`]. |
| +pub struct LayoutError; |
| + |
| +/// A layout for an array `[T; n]`. |
| +/// |
| +/// # Invariants |
| +/// |
| +/// - `len * size_of::<T>() <= isize::MAX`. |
| +pub struct ArrayLayout<T> { |
| + len: usize, |
| + _phantom: PhantomData<fn() -> T>, |
| +} |
| + |
| +impl<T> Clone for ArrayLayout<T> { |
| + fn clone(&self) -> Self { |
| + *self |
| + } |
| +} |
| +impl<T> Copy for ArrayLayout<T> {} |
| + |
| +const ISIZE_MAX: usize = isize::MAX as usize; |
| + |
| +impl<T> ArrayLayout<T> { |
| + /// Creates a new layout for `[T; 0]`. |
| + pub const fn empty() -> Self { |
| + // INVARIANT: `0 * size_of::<T>() <= isize::MAX`. |
| + Self { |
| + len: 0, |
| + _phantom: PhantomData, |
| + } |
| + } |
| + |
| + /// Creates a new layout for `[T; len]`. |
| + /// |
| + /// # Errors |
| + /// |
| + /// When `len * size_of::<T>()` overflows or when `len * size_of::<T>() > isize::MAX`. |
| + pub const fn new(len: usize) -> Result<Self, LayoutError> { |
| + match len.checked_mul(core::mem::size_of::<T>()) { |
| + Some(len) if len <= ISIZE_MAX => { |
| + // INVARIANT: We checked above that `len * size_of::<T>() <= isize::MAX`. |
| + Ok(Self { |
| + len, |
| + _phantom: PhantomData, |
| + }) |
| + } |
| + _ => Err(LayoutError), |
| + } |
| + } |
| + |
| + /// Creates a new layout for `[T; len]`. |
| + /// |
| + /// # Safety |
| + /// |
| + /// `len` must be a value, for which `len * size_of::<T>() <= isize::MAX` is true. |
| + pub unsafe fn new_unchecked(len: usize) -> Self { |
| + // INVARIANT: By the safety requirements of this function |
| + // `len * size_of::<T>() <= isize::MAX`. |
| + Self { |
| + len, |
| + _phantom: PhantomData, |
| + } |
| + } |
| + |
| + /// Returns the number of array elements represented by this layout. |
| + pub const fn len(&self) -> usize { |
| + self.len |
| + } |
| + |
| + /// Returns `true` when no array elements are represented by this layout. |
| + pub const fn is_empty(&self) -> bool { |
| + self.len == 0 |
| + } |
| +} |
| + |
| +impl<T> From<ArrayLayout<T>> for Layout { |
| + fn from(value: ArrayLayout<T>) -> Self { |
| + let res = Layout::array::<T>(value.len); |
| + // SAFETY: By the type invariant of `ArrayLayout` we have |
| + // `len * size_of::<T>() <= isize::MAX` and thus the result must be `Ok`. |
| + unsafe { res.unwrap_unchecked() } |
| + } |
| +} |