| From stable+bounces-121483-greg=kroah.com@vger.kernel.org Fri Mar 7 23:52:08 2025 |
| From: Miguel Ojeda <ojeda@kernel.org> |
| Date: Fri, 7 Mar 2025 23:49:35 +0100 |
| Subject: rust: alloc: implement `KVmalloc` 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-29-ojeda@kernel.org> |
| |
| From: Danilo Krummrich <dakr@kernel.org> |
| |
| commit 8362c2608ba1be635ffa22a256dfcfe51c6238cc upstream. |
| |
| Implement `Allocator` for `KVmalloc`, an `Allocator` that tries to |
| allocate memory with `kmalloc` first and, on failure, falls back to |
| `vmalloc`. |
| |
| All memory allocations made with `KVmalloc` end up in |
| `kvrealloc_noprof()`; all frees in `kvfree()`. |
| |
| 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-10-dakr@kernel.org |
| [ Reworded typo. - Miguel ] |
| Signed-off-by: Miguel Ojeda <ojeda@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| rust/helpers/slab.c | 6 ++++++ |
| rust/kernel/alloc/allocator.rs | 36 ++++++++++++++++++++++++++++++++++++ |
| rust/kernel/alloc/allocator_test.rs | 1 + |
| 3 files changed, 43 insertions(+) |
| |
| --- a/rust/helpers/slab.c |
| +++ b/rust/helpers/slab.c |
| @@ -7,3 +7,9 @@ rust_helper_krealloc(const void *objp, s |
| { |
| return krealloc(objp, new_size, flags); |
| } |
| + |
| +void * __must_check __realloc_size(2) |
| +rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags) |
| +{ |
| + return kvrealloc(p, size, flags); |
| +} |
| --- a/rust/kernel/alloc/allocator.rs |
| +++ b/rust/kernel/alloc/allocator.rs |
| @@ -34,6 +34,15 @@ pub struct Kmalloc; |
| /// For more details see [self]. |
| pub struct Vmalloc; |
| |
| +/// The kvmalloc kernel allocator. |
| +/// |
| +/// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon |
| +/// failure. This allocator is typically used when the size for the requested allocation is not |
| +/// known and may exceed the capabilities of `Kmalloc`. |
| +/// |
| +/// For more details see [self]. |
| +pub struct KVmalloc; |
| + |
| /// 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. |
| @@ -76,6 +85,9 @@ impl ReallocFunc { |
| // INVARIANT: `vrealloc` satisfies the type invariants. |
| const VREALLOC: Self = Self(bindings::vrealloc); |
| |
| + // INVARIANT: `kvrealloc` satisfies the type invariants. |
| + const KVREALLOC: Self = Self(bindings::kvrealloc); |
| + |
| /// # Safety |
| /// |
| /// This method has the same safety requirements as [`Allocator::realloc`]. |
| @@ -205,6 +217,30 @@ unsafe impl Allocator for Vmalloc { |
| } |
| } |
| |
| +// 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 KVmalloc { |
| + #[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!("KVmalloc 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::KVREALLOC.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 |
| @@ -8,6 +8,7 @@ use core::ptr::NonNull; |
| |
| pub struct Kmalloc; |
| pub type Vmalloc = Kmalloc; |
| +pub type KVmalloc = Kmalloc; |
| |
| unsafe impl Allocator for Kmalloc { |
| unsafe fn realloc( |