| From stable+bounces-121482-greg=kroah.com@vger.kernel.org Fri Mar 7 23:51:58 2025 |
| From: Miguel Ojeda <ojeda@kernel.org> |
| Date: Fri, 7 Mar 2025 23:49:34 +0100 |
| Subject: rust: alloc: implement `Vmalloc` allocator |
| 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-28-ojeda@kernel.org> |
| |
| From: Danilo Krummrich <dakr@kernel.org> |
| |
| commit 61c004781d6b928443052e7a6cf84b35d4f61401 upstream. |
| |
| Implement `Allocator` for `Vmalloc`, the kernel's virtually contiguous |
| allocator, typically used for larger objects, (much) larger than page |
| size. |
| |
| All memory allocations made with `Vmalloc` end up in `vrealloc()`. |
| |
| Reviewed-by: Alice Ryhl <aliceryhl@google.com> |
| Reviewed-by: Benno Lossin <benno.lossin@proton.me> |
| Reviewed-by: Gary Guo <gary@garyguo.net> |
| Signed-off-by: Danilo Krummrich <dakr@kernel.org> |
| Link: https://lore.kernel.org/r/20241004154149.93856-9-dakr@kernel.org |
| Signed-off-by: Miguel Ojeda <ojeda@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| rust/helpers/helpers.c | 1 |
| rust/helpers/vmalloc.c | 9 ++++++++ |
| rust/kernel/alloc/allocator.rs | 37 ++++++++++++++++++++++++++++++++++++ |
| rust/kernel/alloc/allocator_test.rs | 1 |
| 4 files changed, 48 insertions(+) |
| create mode 100644 rust/helpers/vmalloc.c |
| |
| --- a/rust/helpers/helpers.c |
| +++ b/rust/helpers/helpers.c |
| @@ -22,5 +22,6 @@ |
| #include "spinlock.c" |
| #include "task.c" |
| #include "uaccess.c" |
| +#include "vmalloc.c" |
| #include "wait.c" |
| #include "workqueue.c" |
| --- /dev/null |
| +++ b/rust/helpers/vmalloc.c |
| @@ -0,0 +1,9 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| + |
| +#include <linux/vmalloc.h> |
| + |
| +void * __must_check __realloc_size(2) |
| +rust_helper_vrealloc(const void *p, size_t size, gfp_t flags) |
| +{ |
| + return vrealloc(p, size, flags); |
| +} |
| --- a/rust/kernel/alloc/allocator.rs |
| +++ b/rust/kernel/alloc/allocator.rs |
| @@ -15,6 +15,7 @@ use core::ptr::NonNull; |
| |
| use crate::alloc::{AllocError, Allocator}; |
| use crate::bindings; |
| +use crate::pr_warn; |
| |
| /// The contiguous kernel allocator. |
| /// |
| @@ -24,6 +25,15 @@ use crate::bindings; |
| /// For more details see [self]. |
| pub struct Kmalloc; |
| |
| +/// The virtually contiguous kernel allocator. |
| +/// |
| +/// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel |
| +/// virtual space. It is typically used for large allocations. The memory allocated with this |
| +/// allocator is not physically contiguous. |
| +/// |
| +/// For more details see [self]. |
| +pub struct Vmalloc; |
| + |
| /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment. |
| fn aligned_size(new_layout: Layout) -> usize { |
| // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. |
| @@ -63,6 +73,9 @@ impl ReallocFunc { |
| // INVARIANT: `krealloc` satisfies the type invariants. |
| const KREALLOC: Self = Self(bindings::krealloc); |
| |
| + // INVARIANT: `vrealloc` satisfies the type invariants. |
| + const VREALLOC: Self = Self(bindings::vrealloc); |
| + |
| /// # Safety |
| /// |
| /// This method has the same safety requirements as [`Allocator::realloc`]. |
| @@ -168,6 +181,30 @@ unsafe impl GlobalAlloc for Kmalloc { |
| } |
| } |
| |
| +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that |
| +// - memory remains valid until it is explicitly freed, |
| +// - passing a pointer to a valid memory allocation is OK, |
| +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. |
| +unsafe impl Allocator for Vmalloc { |
| + #[inline] |
| + unsafe fn realloc( |
| + ptr: Option<NonNull<u8>>, |
| + layout: Layout, |
| + old_layout: Layout, |
| + flags: Flags, |
| + ) -> Result<NonNull<[u8]>, AllocError> { |
| + // TODO: Support alignments larger than PAGE_SIZE. |
| + if layout.align() > bindings::PAGE_SIZE { |
| + pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n"); |
| + return Err(AllocError); |
| + } |
| + |
| + // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously |
| + // allocated with this `Allocator`. |
| + unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) } |
| + } |
| +} |
| + |
| #[global_allocator] |
| static ALLOCATOR: Kmalloc = Kmalloc; |
| |
| --- a/rust/kernel/alloc/allocator_test.rs |
| +++ b/rust/kernel/alloc/allocator_test.rs |
| @@ -7,6 +7,7 @@ use core::alloc::Layout; |
| use core::ptr::NonNull; |
| |
| pub struct Kmalloc; |
| +pub type Vmalloc = Kmalloc; |
| |
| unsafe impl Allocator for Kmalloc { |
| unsafe fn realloc( |