From 31d94d8f586df55e7dce47a67a8428f46d09f906 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:35:54 -0300 Subject: rust: kernel: move `allocator` module under `alloc` We will add more to the `alloc` module in subsequent patches (e.g., allocation flags and extension traits). Reviewed-by: Benno Lossin Signed-off-by: Wedson Almeida Filho Link: https://lore.kernel.org/r/20240328013603.206764-2-wedsonaf@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/alloc/allocator.rs | 88 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 rust/kernel/alloc/allocator.rs (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs new file mode 100644 index 000000000000..01ad139e19bc --- /dev/null +++ b/rust/kernel/alloc/allocator.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Allocator support. + +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr; + +use crate::bindings; + +struct KernelAllocator; + +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. +/// +/// # Safety +/// +/// - `ptr` can be either null or a pointer which has been allocated by this allocator. +/// - `new_layout` must have a non-zero size. +unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gfp_t) -> *mut u8 { + // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. + let layout = new_layout.pad_to_align(); + + let mut size = layout.size(); + + if layout.align() > bindings::ARCH_SLAB_MINALIGN { + // The alignment requirement exceeds the slab guarantee, thus try to enlarge the size + // to use the "power-of-two" size/alignment guarantee (see comments in `kmalloc()` for + // more information). + // + // Note that `layout.size()` (after padding) is guaranteed to be a multiple of + // `layout.align()`, so `next_power_of_two` gives enough alignment guarantee. + size = size.next_power_of_two(); + } + + // SAFETY: + // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the + // function safety requirement. + // - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero + // according to the function safety requirement) or a result from `next_power_of_two()`. + unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 } +} + +unsafe impl GlobalAlloc for KernelAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety + // requirement. + unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + unsafe { + bindings::kfree(ptr as *const core::ffi::c_void); + } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: + // - `new_size`, when rounded up to the nearest multiple of `layout.align()`, will not + // overflow `isize` by the function safety requirement. + // - `layout.align()` is a proper alignment (i.e. not zero and must be a power of two). + let layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + + // SAFETY: + // - `ptr` is either null or a pointer allocated by this allocator by the function safety + // requirement. + // - the size of `layout` is not zero because `new_size` is not zero by the function safety + // requirement. + unsafe { krealloc_aligned(ptr, layout, bindings::GFP_KERNEL) } + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety + // requirement. + unsafe { + krealloc_aligned( + ptr::null_mut(), + layout, + bindings::GFP_KERNEL | bindings::__GFP_ZERO, + ) + } + } +} + +#[global_allocator] +static ALLOCATOR: KernelAllocator = KernelAllocator; + +// See . +#[no_mangle] +static __rust_no_alloc_shim_is_unstable: u8 = 0; -- cgit From 03989773a94490383b062912feb0c4d175f20845 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:35:55 -0300 Subject: rust: alloc: introduce the `VecExt` trait Make `try_with_capacity`, `try_push`, and `try_extend_from_slice` methods available in `Vec` even though it doesn't implement them. It is implemented with `try_reserve` and `push_within_capacity`. This is in preparation for switching to the upstream `alloc` crate. Reviewed-by: Benno Lossin Suggested-by: Gary Guo Signed-off-by: Wedson Almeida Filho Link: https://lore.kernel.org/r/20240328013603.206764-3-wedsonaf@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/alloc.rs | 1 + rust/kernel/alloc/vec_ext.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + rust/kernel/prelude.rs | 2 ++ 4 files changed, 52 insertions(+) create mode 100644 rust/kernel/alloc/vec_ext.rs (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index 9aa5077d4a4c..2c99635f9fd3 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -5,3 +5,4 @@ #[cfg(not(test))] #[cfg(not(testlib))] mod allocator; +pub mod vec_ext; diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs new file mode 100644 index 000000000000..311e62cc5784 --- /dev/null +++ b/rust/kernel/alloc/vec_ext.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Extensions to [`Vec`] for fallible allocations. + +use alloc::{collections::TryReserveError, vec::Vec}; +use core::result::Result; + +/// Extensions to [`Vec`]. +pub trait VecExt: Sized { + /// Creates a new [`Vec`] instance with at least the given capacity. + fn try_with_capacity(capacity: usize) -> Result; + + /// Appends an element to the back of the [`Vec`] instance. + fn try_push(&mut self, v: T) -> Result<(), TryReserveError>; + + /// Pushes clones of the elements of slice into the [`Vec`] instance. + fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveError> + where + T: Clone; +} + +impl VecExt for Vec { + fn try_with_capacity(capacity: usize) -> Result { + let mut v = Vec::new(); + v.try_reserve(capacity)?; + Ok(v) + } + + fn try_push(&mut self, v: T) -> Result<(), TryReserveError> { + if let Err(retry) = self.push_within_capacity(v) { + self.try_reserve(1)?; + let _ = self.push_within_capacity(retry); + } + Ok(()) + } + + fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveError> + where + T: Clone, + { + self.try_reserve(other.len())?; + for item in other { + self.try_push(item.clone())?; + } + + Ok(()) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 1e910fe7c2c7..d3d345aed218 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,6 +18,7 @@ #![feature(new_uninit)] #![feature(receiver_trait)] #![feature(unsize)] +#![feature(vec_push_within_capacity)] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index ae21600970b3..c85b5972c0d3 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -14,6 +14,8 @@ #[doc(no_inline)] pub use core::pin::Pin; +pub use crate::alloc::vec_ext::VecExt; + #[doc(no_inline)] pub use alloc::{boxed::Box, vec::Vec}; -- cgit From b6a006e21b822d1dd262fa249ff71a2991e7b319 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:35:58 -0300 Subject: rust: alloc: introduce allocation flags We'll use them when allocating `Box`, `Arc`, and `UniqueArc` instances, as well as when allocating memory for `Vec` elements. These changes will come in subsequent patches. Reviewed-by: Benno Lossin Signed-off-by: Wedson Almeida Filho Link: https://lore.kernel.org/r/20240328013603.206764-6-wedsonaf@gmail.com Signed-off-by: Miguel Ojeda --- rust/bindings/bindings_helper.h | 3 ++ rust/kernel/alloc.rs | 61 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/alloc/allocator.rs | 17 ++++-------- rust/kernel/prelude.rs | 2 +- 4 files changed, 71 insertions(+), 12 deletions(-) (limited to 'rust/kernel/alloc') diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 65b98831b975..ddb5644d4fd9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -20,5 +20,8 @@ /* `bindgen` gets confused at certain things. */ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; +const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL; +const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT; +const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT; const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO; diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index 2c99635f9fd3..fd5541991a22 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -6,3 +6,64 @@ #[cfg(not(testlib))] mod allocator; pub mod vec_ext; + +/// Flags to be used when allocating memory. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the [`flags`] module. +#[derive(Clone, Copy)] +pub struct Flags(u32); + +impl core::ops::BitOr for Flags { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for Flags { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for Flags { + type Output = Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +/// Allocation flags. +/// +/// These are meant to be used in functions that can allocate memory. +pub mod flags { + use super::Flags; + use crate::bindings; + + /// Zeroes out the allocated memory. + /// + /// This is normally or'd with other flags. + pub const __GFP_ZERO: Flags = Flags(bindings::__GFP_ZERO); + + /// Users can not sleep and need the allocation to succeed. + /// + /// A lower watermark is applied to allow access to "atomic reserves". The current + /// implementation doesn't support NMI and few other strict non-preemptive contexts (e.g. + /// raw_spin_lock). The same applies to [`GFP_NOWAIT`]. + pub const GFP_ATOMIC: Flags = Flags(bindings::GFP_ATOMIC); + + /// Typical for kernel-internal allocations. The caller requires ZONE_NORMAL or a lower zone + /// for direct access but can direct reclaim. + pub const GFP_KERNEL: Flags = Flags(bindings::GFP_KERNEL); + + /// The same as [`GFP_KERNEL`], except the allocation is accounted to kmemcg. + pub const GFP_KERNEL_ACCOUNT: Flags = Flags(bindings::GFP_KERNEL_ACCOUNT); + + /// Ror kernel allocations that should not stall for direct reclaim, start physical IO or + /// use any filesystem callback. It is very likely to fail to allocate memory, even for very + /// small allocations. + pub const GFP_NOWAIT: Flags = Flags(bindings::GFP_NOWAIT); +} diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs index 01ad139e19bc..8cc7099d6ae1 100644 --- a/rust/kernel/alloc/allocator.rs +++ b/rust/kernel/alloc/allocator.rs @@ -2,6 +2,7 @@ //! Allocator support. +use super::{flags::*, Flags}; use core::alloc::{GlobalAlloc, Layout}; use core::ptr; @@ -15,7 +16,7 @@ struct KernelAllocator; /// /// - `ptr` can be either null or a pointer which has been allocated by this allocator. /// - `new_layout` must have a non-zero size. -unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gfp_t) -> *mut u8 { +unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 { // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. let layout = new_layout.pad_to_align(); @@ -36,14 +37,14 @@ unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gf // function safety requirement. // - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero // according to the function safety requirement) or a result from `next_power_of_two()`. - unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 } + unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 } } unsafe impl GlobalAlloc for KernelAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety // requirement. - unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) } + unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL) } } unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { @@ -64,19 +65,13 @@ unsafe impl GlobalAlloc for KernelAllocator { // requirement. // - the size of `layout` is not zero because `new_size` is not zero by the function safety // requirement. - unsafe { krealloc_aligned(ptr, layout, bindings::GFP_KERNEL) } + unsafe { krealloc_aligned(ptr, layout, GFP_KERNEL) } } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety // requirement. - unsafe { - krealloc_aligned( - ptr::null_mut(), - layout, - bindings::GFP_KERNEL | bindings::__GFP_ZERO, - ) - } + unsafe { krealloc_aligned(ptr::null_mut(), layout, GFP_KERNEL | __GFP_ZERO) } } } diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index c85b5972c0d3..827e4dfd77df 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -14,7 +14,7 @@ #[doc(no_inline)] pub use core::pin::Pin; -pub use crate::alloc::vec_ext::VecExt; +pub use crate::alloc::{flags::*, vec_ext::VecExt}; #[doc(no_inline)] pub use alloc::{boxed::Box, vec::Vec}; -- cgit From 08d3f54928796557fc832467ad54f04908fc14e4 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:35:59 -0300 Subject: rust: alloc: introduce the `BoxExt` trait Make fallible versions of `new` and `new_uninit` methods available in `Box` even though it doesn't implement them because we build `alloc` with the `no_global_oom_handling` config. They also have an extra `flags` parameter that allows callers to pass flags to the allocator. Signed-off-by: Wedson Almeida Filho Reviewed-by: Boqun Feng Reviewed-by: Benno Lossin Link: https://lore.kernel.org/r/20240328013603.206764-7-wedsonaf@gmail.com [ Used `Box::write()` to avoid one `unsafe` block as suggested by Boqun. ] Signed-off-by: Miguel Ojeda --- rust/kernel/alloc.rs | 1 + rust/kernel/alloc/allocator.rs | 2 +- rust/kernel/alloc/box_ext.rs | 58 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/init.rs | 13 +++++----- rust/kernel/prelude.rs | 2 +- rust/kernel/sync/arc.rs | 3 ++- 6 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 rust/kernel/alloc/box_ext.rs (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index fd5541991a22..9bc1b48b5641 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -5,6 +5,7 @@ #[cfg(not(test))] #[cfg(not(testlib))] mod allocator; +pub mod box_ext; pub mod vec_ext; /// Flags to be used when allocating memory. diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs index 8cc7099d6ae1..ff88bce04fd4 100644 --- a/rust/kernel/alloc/allocator.rs +++ b/rust/kernel/alloc/allocator.rs @@ -16,7 +16,7 @@ struct KernelAllocator; /// /// - `ptr` can be either null or a pointer which has been allocated by this allocator. /// - `new_layout` must have a non-zero size. -unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 { +pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: Flags) -> *mut u8 { // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. let layout = new_layout.pad_to_align(); diff --git a/rust/kernel/alloc/box_ext.rs b/rust/kernel/alloc/box_ext.rs new file mode 100644 index 000000000000..76653d6f4257 --- /dev/null +++ b/rust/kernel/alloc/box_ext.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Extensions to [`Box`] for fallible allocations. + +use super::Flags; +use alloc::boxed::Box; +use core::alloc::AllocError; +use core::mem::MaybeUninit; +use core::result::Result; + +/// Extensions to [`Box`]. +pub trait BoxExt: Sized { + /// Allocates a new box. + /// + /// The allocation may fail, in which case an error is returned. + fn new(x: T, flags: Flags) -> Result; + + /// Allocates a new uninitialised box. + /// + /// The allocation may fail, in which case an error is returned. + fn new_uninit(flags: Flags) -> Result>, AllocError>; +} + +impl BoxExt for Box { + fn new(x: T, flags: Flags) -> Result { + let b = >::new_uninit(flags)?; + Ok(Box::write(b, x)) + } + + #[cfg(any(test, testlib))] + fn new_uninit(_flags: Flags) -> Result>, AllocError> { + Ok(Box::new_uninit()) + } + + #[cfg(not(any(test, testlib)))] + fn new_uninit(flags: Flags) -> Result>, AllocError> { + let ptr = if core::mem::size_of::>() == 0 { + core::ptr::NonNull::<_>::dangling().as_ptr() + } else { + let layout = core::alloc::Layout::new::>(); + + // SAFETY: Memory is being allocated (first arg is null). The only other source of + // safety issues is sleeping on atomic context, which is addressed by klint. Lastly, + // the type is not a SZT (checked above). + let ptr = + unsafe { super::allocator::krealloc_aligned(core::ptr::null_mut(), layout, flags) }; + if ptr.is_null() { + return Err(AllocError); + } + + ptr.cast::>() + }; + + // SAFETY: For non-zero-sized types, we allocate above using the global allocator. For + // zero-sized types, we use `NonNull::dangling`. + Ok(unsafe { Box::from_raw(ptr) }) + } +} diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 424257284d16..9c798cffc8e4 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -210,6 +210,7 @@ //! [`pin_init!`]: crate::pin_init! use crate::{ + alloc::{box_ext::BoxExt, flags::*}, error::{self, Error}, sync::UniqueArc, types::{Opaque, ScopeGuard}, @@ -305,9 +306,9 @@ macro_rules! stack_pin_init { /// /// stack_try_pin_init!(let foo: Result, AllocError> = pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::try_new(Bar { +/// b: Box::new(Bar { /// x: 64, -/// })?, +/// }, GFP_KERNEL)?, /// })); /// let foo = foo.unwrap(); /// pr_info!("a: {}", &*foo.a.lock()); @@ -331,9 +332,9 @@ macro_rules! stack_pin_init { /// /// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo { /// a <- new_mutex!(42), -/// b: Box::try_new(Bar { +/// b: Box::new(Bar { /// x: 64, -/// })?, +/// }, GFP_KERNEL)?, /// })); /// pr_info!("a: {}", &*foo.a.lock()); /// # Ok::<_, AllocError>(()) @@ -1158,7 +1159,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = Box::try_new_uninit()?; + let mut this = as BoxExt<_>>::new_uninit(GFP_KERNEL)?; let slot = this.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. @@ -1172,7 +1173,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = Box::try_new_uninit()?; + let mut this = as BoxExt<_>>::new_uninit(GFP_KERNEL)?; let slot = this.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index 827e4dfd77df..b37a0b3180fb 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -14,7 +14,7 @@ #[doc(no_inline)] pub use core::pin::Pin; -pub use crate::alloc::{flags::*, vec_ext::VecExt}; +pub use crate::alloc::{box_ext::BoxExt, flags::*, vec_ext::VecExt}; #[doc(no_inline)] pub use alloc::{boxed::Box, vec::Vec}; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 7d4c4bf58388..1252a1b630ed 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -16,6 +16,7 @@ //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html use crate::{ + alloc::{box_ext::BoxExt, flags::*}, bindings, error::{self, Error}, init::{self, InPlaceInit, Init, PinInit}, @@ -170,7 +171,7 @@ impl Arc { data: contents, }; - let inner = Box::try_new(value)?; + let inner = as BoxExt<_>>::new(value, GFP_KERNEL)?; // SAFETY: We just created `inner` with a reference count of 1, which is owned by the new // `Arc` object. -- cgit From 5ab560ce12ed0df3450968cfe4211e398ff2a8d7 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:36:00 -0300 Subject: rust: alloc: update `VecExt` to take allocation flags We also rename the methods by removing the `try_` prefix since the names are available due to our usage of the `no_global_oom_handling` config when building the `alloc` crate. Reviewed-by: Boqun Feng Signed-off-by: Wedson Almeida Filho Reviewed-by: Benno Lossin Link: https://lore.kernel.org/r/20240328013603.206764-8-wedsonaf@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/alloc/vec_ext.rs | 158 +++++++++++++++++++++++++++++++++++++++---- rust/kernel/error.rs | 11 +-- rust/kernel/lib.rs | 1 - rust/kernel/str.rs | 6 +- rust/kernel/types.rs | 4 +- samples/rust/rust_minimal.rs | 6 +- 6 files changed, 152 insertions(+), 34 deletions(-) (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs index 311e62cc5784..e24d7c7675ca 100644 --- a/rust/kernel/alloc/vec_ext.rs +++ b/rust/kernel/alloc/vec_ext.rs @@ -2,47 +2,175 @@ //! Extensions to [`Vec`] for fallible allocations. -use alloc::{collections::TryReserveError, vec::Vec}; +use super::Flags; +use alloc::{alloc::AllocError, vec::Vec}; use core::result::Result; /// Extensions to [`Vec`]. pub trait VecExt: Sized { /// Creates a new [`Vec`] instance with at least the given capacity. - fn try_with_capacity(capacity: usize) -> Result; + /// + /// # Examples + /// + /// ``` + /// let v = Vec::::with_capacity(20, GFP_KERNEL)?; + /// + /// assert!(v.capacity() >= 20); + /// # Ok::<(), Error>(()) + /// ``` + fn with_capacity(capacity: usize, flags: Flags) -> Result; /// Appends an element to the back of the [`Vec`] instance. - fn try_push(&mut self, v: T) -> Result<(), TryReserveError>; + /// + /// # Examples + /// + /// ``` + /// let mut v = Vec::new(); + /// v.push(1, GFP_KERNEL)?; + /// assert_eq!(&v, &[1]); + /// + /// v.push(2, GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 2]); + /// # Ok::<(), Error>(()) + /// ``` + fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError>; /// Pushes clones of the elements of slice into the [`Vec`] instance. - fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveError> + /// + /// # Examples + /// + /// ``` + /// let mut v = Vec::new(); + /// v.push(1, GFP_KERNEL)?; + /// + /// v.extend_from_slice(&[20, 30, 40], GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 20, 30, 40]); + /// + /// v.extend_from_slice(&[50, 60], GFP_KERNEL)?; + /// assert_eq!(&v, &[1, 20, 30, 40, 50, 60]); + /// # Ok::<(), Error>(()) + /// ``` + fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError> where T: Clone; + + /// Ensures that the capacity exceeds the length by at least `additional` elements. + /// + /// # Examples + /// + /// ``` + /// let mut v = Vec::new(); + /// v.push(1, GFP_KERNEL)?; + /// + /// v.reserve(10, GFP_KERNEL)?; + /// let cap = v.capacity(); + /// assert!(cap >= 10); + /// + /// v.reserve(10, GFP_KERNEL)?; + /// let new_cap = v.capacity(); + /// assert_eq!(new_cap, cap); + /// + /// # Ok::<(), Error>(()) + /// ``` + fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError>; } impl VecExt for Vec { - fn try_with_capacity(capacity: usize) -> Result { + fn with_capacity(capacity: usize, flags: Flags) -> Result { let mut v = Vec::new(); - v.try_reserve(capacity)?; + >::reserve(&mut v, capacity, flags)?; Ok(v) } - fn try_push(&mut self, v: T) -> Result<(), TryReserveError> { - if let Err(retry) = self.push_within_capacity(v) { - self.try_reserve(1)?; - let _ = self.push_within_capacity(retry); - } + fn push(&mut self, v: T, flags: Flags) -> Result<(), AllocError> { + >::reserve(self, 1, flags)?; + let s = self.spare_capacity_mut(); + s[0].write(v); + + // SAFETY: We just initialised the first spare entry, so it is safe to increase the length + // by 1. We also know that the new length is <= capacity because of the previous call to + // `reserve` above. + unsafe { self.set_len(self.len() + 1) }; Ok(()) } - fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveError> + fn extend_from_slice(&mut self, other: &[T], flags: Flags) -> Result<(), AllocError> where T: Clone, { - self.try_reserve(other.len())?; - for item in other { - self.try_push(item.clone())?; + >::reserve(self, other.len(), flags)?; + for (slot, item) in core::iter::zip(self.spare_capacity_mut(), other) { + slot.write(item.clone()); } + // SAFETY: We just initialised the `other.len()` spare entries, so it is safe to increase + // the length by the same amount. We also know that the new length is <= capacity because + // of the previous call to `reserve` above. + unsafe { self.set_len(self.len() + other.len()) }; + Ok(()) + } + + #[cfg(any(test, testlib))] + fn reserve(&mut self, additional: usize, _flags: Flags) -> Result<(), AllocError> { + Vec::reserve(self, additional); Ok(()) } + + #[cfg(not(any(test, testlib)))] + fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocError> { + let len = self.len(); + let cap = self.capacity(); + + if cap - len >= additional { + return Ok(()); + } + + if core::mem::size_of::() == 0 { + // The capacity is already `usize::MAX` for SZTs, we can't go higher. + return Err(AllocError); + } + + // We know cap is <= `isize::MAX` because `Layout::array` fails if the resulting byte size + // is greater than `isize::MAX`. So the multiplication by two won't overflow. + let new_cap = core::cmp::max(cap * 2, len.checked_add(additional).ok_or(AllocError)?); + let layout = core::alloc::Layout::array::(new_cap).map_err(|_| AllocError)?; + + let (ptr, len, cap) = destructure(self); + + // SAFETY: `ptr` is valid because it's either NULL or comes from a previous call to + // `krealloc_aligned`. We also verified that the type is not a ZST. + let new_ptr = unsafe { super::allocator::krealloc_aligned(ptr.cast(), layout, flags) }; + if new_ptr.is_null() { + // SAFETY: We are just rebuilding the existing `Vec` with no changes. + unsafe { rebuild(self, ptr, len, cap) }; + Err(AllocError) + } else { + // SAFETY: `ptr` has been reallocated with the layout for `new_cap` elements. New cap + // is greater than `cap`, so it continues to be >= `len`. + unsafe { rebuild(self, new_ptr.cast::(), len, new_cap) }; + Ok(()) + } + } +} + +#[cfg(not(any(test, testlib)))] +fn destructure(v: &mut Vec) -> (*mut T, usize, usize) { + let mut tmp = Vec::new(); + core::mem::swap(&mut tmp, v); + let mut tmp = core::mem::ManuallyDrop::new(tmp); + let len = tmp.len(); + let cap = tmp.capacity(); + (tmp.as_mut_ptr(), len, cap) +} + +/// Rebuilds a `Vec` from a pointer, length, and capacity. +/// +/// # Safety +/// +/// The same as [`Vec::from_raw_parts`]. +#[cfg(not(any(test, testlib)))] +unsafe fn rebuild(v: &mut Vec, ptr: *mut T, len: usize, cap: usize) { + // SAFETY: The safety requirements from this function satisfy those of `from_raw_parts`. + let mut tmp = unsafe { Vec::from_raw_parts(ptr, len, cap) }; + core::mem::swap(&mut tmp, v); } diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 4786d3ee1e92..e53466937796 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -6,10 +6,7 @@ use crate::str::CStr; -use alloc::{ - alloc::{AllocError, LayoutError}, - collections::TryReserveError, -}; +use alloc::alloc::{AllocError, LayoutError}; use core::convert::From; use core::fmt; @@ -192,12 +189,6 @@ impl From for Error { } } -impl From for Error { - fn from(_: TryReserveError) -> Error { - code::ENOMEM - } -} - impl From for Error { fn from(_: LayoutError) -> Error { code::ENOMEM diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index d3d345aed218..1e910fe7c2c7 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,7 +18,6 @@ #![feature(new_uninit)] #![feature(receiver_trait)] #![feature(unsize)] -#![feature(vec_push_within_capacity)] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 14ef4344cf6e..f454252c6215 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -2,7 +2,7 @@ //! String representations. -use crate::alloc::vec_ext::VecExt; +use crate::alloc::{flags::*, vec_ext::VecExt}; use alloc::alloc::AllocError; use alloc::vec::Vec; use core::fmt::{self, Write}; @@ -807,7 +807,7 @@ impl CString { let size = f.bytes_written(); // Allocate a vector with the required number of bytes, and write to it. - let mut buf = Vec::try_with_capacity(size)?; + let mut buf = as VecExt<_>>::with_capacity(size, GFP_KERNEL)?; // SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes. let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) }; f.write_fmt(args)?; @@ -856,7 +856,7 @@ impl<'a> TryFrom<&'a CStr> for CString { fn try_from(cstr: &'a CStr) -> Result { let mut buf = Vec::new(); - buf.try_extend_from_slice(cstr.as_bytes_with_nul()) + as VecExt<_>>::extend_from_slice(&mut buf, cstr.as_bytes_with_nul(), GFP_KERNEL) .map_err(|_| AllocError)?; // INVARIANT: The `CStr` and `CString` types have the same invariants for diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index aa77bad9bce4..8fad61268465 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -157,11 +157,11 @@ impl ForeignOwnable for () { /// let mut vec = /// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len())); /// -/// vec.try_push(10u8)?; +/// vec.push(10u8, GFP_KERNEL)?; /// if arg { /// return Ok(()); /// } -/// vec.try_push(20u8)?; +/// vec.push(20u8, GFP_KERNEL)?; /// Ok(()) /// } /// diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index dc05f4bbe27e..2a9eaab62d1c 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -22,9 +22,9 @@ impl kernel::Module for RustMinimal { pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); let mut numbers = Vec::new(); - numbers.try_push(72)?; - numbers.try_push(108)?; - numbers.try_push(200)?; + numbers.push(72, GFP_KERNEL)?; + numbers.push(108, GFP_KERNEL)?; + numbers.push(200, GFP_KERNEL)?; Ok(RustMinimal { numbers }) } -- cgit From 2c1092853f163762ef0aabc551a630ef233e1be3 Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 27 Mar 2024 22:36:03 -0300 Subject: rust: kernel: remove usage of `allocator_api` unstable feature With the adoption of `BoxExt` and `VecExt`, we don't need the functions provided by this feature (namely the methods prefixed with `try_` and different allocator per collection instance). We do need `AllocError`, but we define our own as it is a trivial empty struct. Reviewed-by: Benno Lossin Signed-off-by: Wedson Almeida Filho Link: https://lore.kernel.org/r/20240328013603.206764-11-wedsonaf@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/alloc.rs | 4 ++++ rust/kernel/alloc/box_ext.rs | 3 +-- rust/kernel/alloc/vec_ext.rs | 4 ++-- rust/kernel/error.rs | 4 ++-- rust/kernel/init.rs | 3 +-- rust/kernel/lib.rs | 1 - rust/kernel/str.rs | 3 +-- rust/kernel/sync/arc.rs | 4 ++-- rust/kernel/workqueue.rs | 3 +-- 9 files changed, 14 insertions(+), 15 deletions(-) (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index 9bc1b48b5641..f1c2c4aa22d2 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -8,6 +8,10 @@ mod allocator; pub mod box_ext; pub mod vec_ext; +/// Indicates an allocation error. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AllocError; + /// Flags to be used when allocating memory. /// /// They can be combined with the operators `|`, `&`, and `!`. diff --git a/rust/kernel/alloc/box_ext.rs b/rust/kernel/alloc/box_ext.rs index 76653d6f4257..cdbb5ad166d9 100644 --- a/rust/kernel/alloc/box_ext.rs +++ b/rust/kernel/alloc/box_ext.rs @@ -2,9 +2,8 @@ //! Extensions to [`Box`] for fallible allocations. -use super::Flags; +use super::{AllocError, Flags}; use alloc::boxed::Box; -use core::alloc::AllocError; use core::mem::MaybeUninit; use core::result::Result; diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs index e24d7c7675ca..6a916fcf8bf1 100644 --- a/rust/kernel/alloc/vec_ext.rs +++ b/rust/kernel/alloc/vec_ext.rs @@ -2,8 +2,8 @@ //! Extensions to [`Vec`] for fallible allocations. -use super::Flags; -use alloc::{alloc::AllocError, vec::Vec}; +use super::{AllocError, Flags}; +use alloc::vec::Vec; use core::result::Result; /// Extensions to [`Vec`]. diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e53466937796..fc986bc24c6d 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -4,9 +4,9 @@ //! //! C header: [`include/uapi/asm-generic/errno-base.h`](srctree/include/uapi/asm-generic/errno-base.h) -use crate::str::CStr; +use crate::{alloc::AllocError, str::CStr}; -use alloc::alloc::{AllocError, LayoutError}; +use alloc::alloc::LayoutError; use core::convert::From; use core::fmt; diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index fec47b274ec3..9608f2bd2211 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -211,14 +211,13 @@ //! [`pin_init!`]: crate::pin_init! use crate::{ - alloc::{box_ext::BoxExt, Flags}, + alloc::{box_ext::BoxExt, AllocError, Flags}, error::{self, Error}, sync::UniqueArc, types::{Opaque, ScopeGuard}, }; use alloc::boxed::Box; use core::{ - alloc::AllocError, cell::UnsafeCell, convert::Infallible, marker::PhantomData, diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 1e910fe7c2c7..9a943d99c71a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -12,7 +12,6 @@ //! do so first instead of bypassing this crate. #![no_std] -#![feature(allocator_api)] #![feature(coerce_unsized)] #![feature(dispatch_from_dyn)] #![feature(new_uninit)] diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index f454252c6215..27641c3e4df8 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -2,8 +2,7 @@ //! String representations. -use crate::alloc::{flags::*, vec_ext::VecExt}; -use alloc::alloc::AllocError; +use crate::alloc::{flags::*, vec_ext::VecExt, AllocError}; use alloc::vec::Vec; use core::fmt::{self, Write}; use core::ops::{self, Deref, DerefMut, Index}; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 0866378f1360..c2a3a2c7cbc5 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -16,7 +16,7 @@ //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html use crate::{ - alloc::{box_ext::BoxExt, Flags}, + alloc::{box_ext::BoxExt, AllocError, Flags}, bindings, error::{self, Error}, init::{self, InPlaceInit, Init, PinInit}, @@ -25,7 +25,7 @@ use crate::{ }; use alloc::boxed::Box; use core::{ - alloc::{AllocError, Layout}, + alloc::Layout, fmt, marker::{PhantomData, Unsize}, mem::{ManuallyDrop, MaybeUninit}, diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index ba5fb05130c5..9f47bad0b003 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -132,9 +132,8 @@ //! //! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h) -use crate::alloc::Flags; +use crate::alloc::{AllocError, Flags}; use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Opaque}; -use alloc::alloc::AllocError; use alloc::boxed::Box; use core::marker::PhantomData; use core::pin::Pin; -- cgit From 00280272a0e5d98055e4d47db38a9b4b5517520e Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 1 Apr 2024 23:23:02 +0200 Subject: rust: kernel: remove redundant imports Rust's `unused_imports` lint covers both unused and redundant imports. In the upcoming 1.78.0, the lint detects more cases of redundant imports [1], e.g.: error: the item `bindings` is imported redundantly --> rust/kernel/print.rs:38:9 | 38 | use crate::bindings; | ^^^^^^^^^^^^^^^ the item `bindings` is already defined by prelude Most cases are `use crate::bindings`, plus a few other items like `Box`. Thus clean them up. Note that, in the `bindings` case, the message "defined by prelude" above means the extern prelude, i.e. the `--extern` flags we pass. Link: https://github.com/rust-lang/rust/pull/117772 [1] Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/r/20240401212303.537355-3-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/alloc.rs | 1 - rust/kernel/alloc/allocator.rs | 2 -- rust/kernel/alloc/box_ext.rs | 1 - rust/kernel/alloc/vec_ext.rs | 1 - rust/kernel/error.rs | 1 - rust/kernel/net/phy.rs | 2 +- rust/kernel/print.rs | 5 ----- rust/kernel/str.rs | 5 +---- rust/kernel/sync/arc.rs | 1 - rust/kernel/sync/condvar.rs | 1 - rust/kernel/sync/lock.rs | 2 +- rust/kernel/sync/lock/mutex.rs | 2 -- rust/kernel/sync/lock/spinlock.rs | 2 -- rust/kernel/task.rs | 2 +- rust/kernel/workqueue.rs | 4 +--- 15 files changed, 5 insertions(+), 27 deletions(-) (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs index f1c2c4aa22d2..531b5e471cb1 100644 --- a/rust/kernel/alloc.rs +++ b/rust/kernel/alloc.rs @@ -46,7 +46,6 @@ impl core::ops::Not for Flags { /// These are meant to be used in functions that can allocate memory. pub mod flags { use super::Flags; - use crate::bindings; /// Zeroes out the allocated memory. /// diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs index ff88bce04fd4..229642960cd1 100644 --- a/rust/kernel/alloc/allocator.rs +++ b/rust/kernel/alloc/allocator.rs @@ -6,8 +6,6 @@ use super::{flags::*, Flags}; use core::alloc::{GlobalAlloc, Layout}; use core::ptr; -use crate::bindings; - struct KernelAllocator; /// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. diff --git a/rust/kernel/alloc/box_ext.rs b/rust/kernel/alloc/box_ext.rs index cdbb5ad166d9..829cb1c1cf9e 100644 --- a/rust/kernel/alloc/box_ext.rs +++ b/rust/kernel/alloc/box_ext.rs @@ -5,7 +5,6 @@ use super::{AllocError, Flags}; use alloc::boxed::Box; use core::mem::MaybeUninit; -use core::result::Result; /// Extensions to [`Box`]. pub trait BoxExt: Sized { diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs index 6a916fcf8bf1..25025a36e250 100644 --- a/rust/kernel/alloc/vec_ext.rs +++ b/rust/kernel/alloc/vec_ext.rs @@ -4,7 +4,6 @@ use super::{AllocError, Flags}; use alloc::vec::Vec; -use core::result::Result; /// Extensions to [`Vec`]. pub trait VecExt: Sized { diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index fc986bc24c6d..55280ae9fe40 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -8,7 +8,6 @@ use crate::{alloc::AllocError, str::CStr}; use alloc::alloc::LayoutError; -use core::convert::From; use core::fmt; use core::num::TryFromIntError; use core::str::Utf8Error; diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 96e09c6e8530..fba19165aa64 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -6,7 +6,7 @@ //! //! C headers: [`include/linux/phy.h`](srctree/include/linux/phy.h). -use crate::{bindings, error::*, prelude::*, str::CStr, types::Opaque}; +use crate::{error::*, prelude::*, types::Opaque}; use core::marker::PhantomData; diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 9b13aca832c2..a78aa3514a0a 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -13,9 +13,6 @@ use core::{ use crate::str::RawFormatter; -#[cfg(CONFIG_PRINTK)] -use crate::bindings; - // Called from `vsprintf` with format specifier `%pA`. #[no_mangle] unsafe extern "C" fn rust_fmt_argument( @@ -35,8 +32,6 @@ unsafe extern "C" fn rust_fmt_argument( /// Public but hidden since it should only be used from public macros. #[doc(hidden)] pub mod format_strings { - use crate::bindings; - /// The length we copy from the `KERN_*` kernel prefixes. const LENGTH_PREFIX: usize = 2; diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 27641c3e4df8..bb8d4f41475b 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -7,10 +7,7 @@ use alloc::vec::Vec; use core::fmt::{self, Write}; use core::ops::{self, Deref, DerefMut, Index}; -use crate::{ - bindings, - error::{code::*, Error}, -}; +use crate::error::{code::*, Error}; /// Byte string without UTF-8 validity guarantee. #[repr(transparent)] diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index a65716ec24a6..3673496c2363 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -17,7 +17,6 @@ use crate::{ alloc::{box_ext::BoxExt, AllocError, Flags}, - bindings, error::{self, Error}, init::{self, InPlaceInit, Init, PinInit}, try_init, diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs index ef6ffef0aa88..2b306afbe56d 100644 --- a/rust/kernel/sync/condvar.rs +++ b/rust/kernel/sync/condvar.rs @@ -7,7 +7,6 @@ use super::{lock::Backend, lock::Guard, LockClassKey}; use crate::{ - bindings, init::PinInit, pin_init, str::CStr, diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs index 5b5c8efe427a..f6c34ca4d819 100644 --- a/rust/kernel/sync/lock.rs +++ b/rust/kernel/sync/lock.rs @@ -6,7 +6,7 @@ //! spinlocks, raw spinlocks) to be provided with minimal effort. use super::LockClassKey; -use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard}; +use crate::{init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard}; use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned}; use macros::pin_data; diff --git a/rust/kernel/sync/lock/mutex.rs b/rust/kernel/sync/lock/mutex.rs index 93e1c982facf..30632070ee67 100644 --- a/rust/kernel/sync/lock/mutex.rs +++ b/rust/kernel/sync/lock/mutex.rs @@ -4,8 +4,6 @@ //! //! This module allows Rust code to use the kernel's `struct mutex`. -use crate::bindings; - /// Creates a [`Mutex`] initialiser with the given name and a newly-created lock class. /// /// It uses the name if one is given, otherwise it generates one based on the file name and line diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs index 6e900807d3b7..ea5c5bc1ce12 100644 --- a/rust/kernel/sync/lock/spinlock.rs +++ b/rust/kernel/sync/lock/spinlock.rs @@ -4,8 +4,6 @@ //! //! This module allows Rust code to use the kernel's `spinlock_t`. -use crate::bindings; - /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class. /// /// It uses the name if one is given, otherwise it generates one based on the file name and line diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs index ca6e7e31d71c..55dff7e088bf 100644 --- a/rust/kernel/task.rs +++ b/rust/kernel/task.rs @@ -4,7 +4,7 @@ //! //! C header: [`include/linux/sched.h`](srctree/include/linux/sched.h). -use crate::{bindings, types::Opaque}; +use crate::types::Opaque; use core::{ ffi::{c_int, c_long, c_uint}, marker::PhantomData, diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index 22813b76861d..1cec63a2aea8 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -131,10 +131,8 @@ //! C header: [`include/linux/workqueue.h`](srctree/include/linux/workqueue.h) use crate::alloc::{AllocError, Flags}; -use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Opaque}; -use alloc::boxed::Box; +use crate::{prelude::*, sync::Arc, sync::LockClassKey, types::Opaque}; use core::marker::PhantomData; -use core::pin::Pin; /// Creates a [`Work`] initialiser with the given name and a newly-created lock class. #[macro_export] -- cgit From 97ab3e8eec0ce79d9e265e6c9e4c480492180409 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 1 May 2024 15:47:43 +0200 Subject: rust: alloc: fix dangling pointer in VecExt::reserve() Currently, a Vec's ptr value, after calling Vec::new(), is initialized to Unique::dangling(). Hence, in VecExt::reserve(), we're passing a dangling pointer (instead of NULL) to krealloc() whenever a new Vec's backing storage is allocated through VecExt extension functions. This only works as long as align_of::(), used by Unique::dangling() to derive the dangling pointer, resolves to a value between 0x0 and ZERO_SIZE_PTR (0x10) and krealloc() hence treats it the same as a NULL pointer however. This isn't a case we should rely on, since there may be types whose alignment may exceed the range still covered by krealloc(), plus other kernel allocators are not as tolerant either. Instead, pass a real NULL pointer to krealloc_aligned() if Vec's capacity is zero. Fixes: 5ab560ce12ed ("rust: alloc: update `VecExt` to take allocation flags") Reviewed-by: Alice Ryhl Reviewed-by: Boqun Feng Reviewed-by: Benno Lossin Signed-off-by: Danilo Krummrich Reviewed-by: Wedson Almeida Filho Link: https://lore.kernel.org/r/20240501134834.22323-1-dakr@redhat.com [ Solved `use` conflict and applied the `if`-instead-of-`match` change discussed in the list. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/alloc/vec_ext.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'rust/kernel/alloc') diff --git a/rust/kernel/alloc/vec_ext.rs b/rust/kernel/alloc/vec_ext.rs index 25025a36e250..e9a81052728a 100644 --- a/rust/kernel/alloc/vec_ext.rs +++ b/rust/kernel/alloc/vec_ext.rs @@ -4,6 +4,7 @@ use super::{AllocError, Flags}; use alloc::vec::Vec; +use core::ptr; /// Extensions to [`Vec`]. pub trait VecExt: Sized { @@ -134,14 +135,20 @@ impl VecExt for Vec { let new_cap = core::cmp::max(cap * 2, len.checked_add(additional).ok_or(AllocError)?); let layout = core::alloc::Layout::array::(new_cap).map_err(|_| AllocError)?; - let (ptr, len, cap) = destructure(self); + let (old_ptr, len, cap) = destructure(self); + + // We need to make sure that `ptr` is either NULL or comes from a previous call to + // `krealloc_aligned`. A `Vec`'s `ptr` value is not guaranteed to be NULL and might be + // dangling after being created with `Vec::new`. Instead, we can rely on `Vec`'s capacity + // to be zero if no memory has been allocated yet. + let ptr = if cap == 0 { ptr::null_mut() } else { old_ptr }; // SAFETY: `ptr` is valid because it's either NULL or comes from a previous call to // `krealloc_aligned`. We also verified that the type is not a ZST. let new_ptr = unsafe { super::allocator::krealloc_aligned(ptr.cast(), layout, flags) }; if new_ptr.is_null() { // SAFETY: We are just rebuilding the existing `Vec` with no changes. - unsafe { rebuild(self, ptr, len, cap) }; + unsafe { rebuild(self, old_ptr, len, cap) }; Err(AllocError) } else { // SAFETY: `ptr` has been reallocated with the layout for `new_cap` elements. New cap -- cgit