Message ID | 20241001150008.183102-16-dakr@kernel.org (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | Generic `Allocator` support for Rust | expand |
On Tue, 1 Oct 2024 16:59:50 +0200 Danilo Krummrich <dakr@kernel.org> wrote: > From: Benno Lossin <benno.lossin@proton.me> > > 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. > > Signed-off-by: Benno Lossin <benno.lossin@proton.me> > Signed-off-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Gary Guo <gary@garyguo.net> > --- > rust/kernel/alloc.rs | 1 + > rust/kernel/alloc/layout.rs | 91 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 92 insertions(+) > create mode 100644 rust/kernel/alloc/layout.rs > > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs > index ebe58247504f..bf143a71d53d 100644 > --- 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))] > diff --git a/rust/kernel/alloc/layout.rs b/rust/kernel/alloc/layout.rs > new file mode 100644 > index 000000000000..a9c987aad8fb > --- /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(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 { nit: this can also be const, although this can be added when need arise (or one may use `new(...).unwrap()` in const context, since it doesn't actually generate a call to `BUG`). > + // 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() } > + } > +}
Hi Danilo,
kernel test robot noticed the following build errors:
[auto build test ERROR on 9852d85ec9d492ebef56dc5f229416c925758edc]
url: https://github.com/intel-lab-lkp/linux/commits/Danilo-Krummrich/rust-alloc-add-Allocator-trait/20241001-230558
base: 9852d85ec9d492ebef56dc5f229416c925758edc
patch link: https://lore.kernel.org/r/20241001150008.183102-16-dakr%40kernel.org
patch subject: [PATCH v8 15/29] rust: alloc: introduce `ArrayLayout`
config: riscv-randconfig-001-20241002 (https://download.01.org/0day-ci/archive/20241003/202410030724.cV9BEoNG-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241003/202410030724.cV9BEoNG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410030724.cV9BEoNG-lkp@intel.com/
All errors (new ones prefixed by >>):
>> error[E0425]: cannot find function `size_of` in this scope
--> rust/kernel/alloc/layout.rs:47:31
|
47 | match len.checked_mul(size_of::<T>()) {
| ^^^^^^^ not found in this scope
|
help: consider importing this function
|
7 + use core::mem::size_of;
|
diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index ebe58247504f..bf143a71d53d 100644 --- 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))] diff --git a/rust/kernel/alloc/layout.rs b/rust/kernel/alloc/layout.rs new file mode 100644 index 000000000000..a9c987aad8fb --- /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(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() } + } +}