// SPDX-License-Identifier: GPL-2.012//! Allocator support.3//!4//! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide"5//! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the6//! typical application of the different kernel allocators.7//!8//! Reference: <https://docs.kernel.org/core-api/memory-allocation.html>910use super::Flags;11use core::alloc::Layout;12use core::ptr;13use core::ptr::NonNull;1415use crate::alloc::{AllocError, Allocator, NumaNode};16use crate::bindings;17use crate::page;1819const ARCH_KMALLOC_MINALIGN: usize = bindings::ARCH_KMALLOC_MINALIGN;2021mod iter;22pub use self::iter::VmallocPageIter;2324/// The contiguous kernel allocator.25///26/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also27/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific.28///29/// For more details see [self].30pub struct Kmalloc;3132/// The virtually contiguous kernel allocator.33///34/// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel35/// virtual space. It is typically used for large allocations. The memory allocated with this36/// allocator is not physically contiguous.37///38/// For more details see [self].39pub struct Vmalloc;4041/// The kvmalloc kernel allocator.42///43/// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon44/// failure. This allocator is typically used when the size for the requested allocation is not45/// known and may exceed the capabilities of `Kmalloc`.46///47/// For more details see [self].48pub struct KVmalloc;4950/// # Invariants51///52/// One of the following: `krealloc_node_align`, `vrealloc_node_align`, `kvrealloc_node_align`.53struct ReallocFunc(54unsafe extern "C" fn(55*const crate::ffi::c_void,56usize,57crate::ffi::c_ulong,58u32,59crate::ffi::c_int,60) -> *mut crate::ffi::c_void,61);6263impl ReallocFunc {64// INVARIANT: `krealloc_node_align` satisfies the type invariants.65const KREALLOC: Self = Self(bindings::krealloc_node_align);6667// INVARIANT: `vrealloc_node_align` satisfies the type invariants.68const VREALLOC: Self = Self(bindings::vrealloc_node_align);6970// INVARIANT: `kvrealloc_node_align` satisfies the type invariants.71const KVREALLOC: Self = Self(bindings::kvrealloc_node_align);7273/// # Safety74///75/// This method has the same safety requirements as [`Allocator::realloc`].76///77/// # Guarantees78///79/// This method has the same guarantees as `Allocator::realloc`. Additionally80/// - it accepts any pointer to a valid memory allocation allocated by this function.81/// - memory allocated by this function remains valid until it is passed to this function.82#[inline]83unsafe fn call(84&self,85ptr: Option<NonNull<u8>>,86layout: Layout,87old_layout: Layout,88flags: Flags,89nid: NumaNode,90) -> Result<NonNull<[u8]>, AllocError> {91let size = layout.size();92let ptr = match ptr {93Some(ptr) => {94if old_layout.size() == 0 {95ptr::null()96} else {97ptr.as_ptr()98}99}100None => ptr::null(),101};102103// SAFETY:104// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that105// `ptr` is NULL or valid.106// - `ptr` is either NULL or valid by the safety requirements of this function.107//108// GUARANTEE:109// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.110// - Those functions provide the guarantees of this function.111let raw_ptr = unsafe {112// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.113self.0(ptr.cast(), size, layout.align(), flags.0, nid.0).cast()114};115116let ptr = if size == 0 {117crate::alloc::dangling_from_layout(layout)118} else {119NonNull::new(raw_ptr).ok_or(AllocError)?120};121122Ok(NonNull::slice_from_raw_parts(ptr, size))123}124}125126impl Kmalloc {127/// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of128/// `layout`.129pub fn aligned_layout(layout: Layout) -> Layout {130// Note that `layout.size()` (after padding) is guaranteed to be a multiple of131// `layout.align()` which together with the slab guarantees means that `Kmalloc` will return132// a properly aligned object (see comments in `kmalloc()` for more information).133layout.pad_to_align()134}135}136137// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that138// - memory remains valid until it is explicitly freed,139// - passing a pointer to a valid memory allocation is OK,140// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.141unsafe impl Allocator for Kmalloc {142const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;143144#[inline]145unsafe fn realloc(146ptr: Option<NonNull<u8>>,147layout: Layout,148old_layout: Layout,149flags: Flags,150nid: NumaNode,151) -> Result<NonNull<[u8]>, AllocError> {152let layout = Kmalloc::aligned_layout(layout);153154// SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.155unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags, nid) }156}157}158159impl Vmalloc {160/// Convert a pointer to a [`Vmalloc`] allocation to a [`page::BorrowedPage`].161///162/// # Examples163///164/// ```165/// # use core::ptr::{NonNull, from_mut};166/// # use kernel::{page, prelude::*};167/// use kernel::alloc::allocator::Vmalloc;168///169/// let mut vbox = VBox::<[u8; page::PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;170///171/// {172/// // SAFETY: By the type invariant of `Box` the inner pointer of `vbox` is non-null.173/// let ptr = unsafe { NonNull::new_unchecked(from_mut(&mut *vbox)) };174///175/// // SAFETY:176/// // `ptr` is a valid pointer to a `Vmalloc` allocation.177/// // `ptr` is valid for the entire lifetime of `page`.178/// let page = unsafe { Vmalloc::to_page(ptr.cast()) };179///180/// // SAFETY: There is no concurrent read or write to the same page.181/// unsafe { page.fill_zero_raw(0, page::PAGE_SIZE)? };182/// }183/// # Ok::<(), Error>(())184/// ```185///186/// # Safety187///188/// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation.189/// - `ptr` must remain valid for the entire duration of `'a`.190pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {191// SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.192let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };193194// SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer195// to `Vmalloc` memory.196let page = unsafe { NonNull::new_unchecked(page) };197198// SAFETY:199// - `page` is a valid pointer to a `struct page`, given that by the safety requirements of200// this function `ptr` is a valid pointer to a `Vmalloc` allocation.201// - By the safety requirements of this function `ptr` is valid for the entire lifetime of202// `'a`.203unsafe { page::BorrowedPage::from_raw(page) }204}205}206207// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that208// - memory remains valid until it is explicitly freed,209// - passing a pointer to a valid memory allocation is OK,210// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.211unsafe impl Allocator for Vmalloc {212const MIN_ALIGN: usize = kernel::page::PAGE_SIZE;213214#[inline]215unsafe fn realloc(216ptr: Option<NonNull<u8>>,217layout: Layout,218old_layout: Layout,219flags: Flags,220nid: NumaNode,221) -> Result<NonNull<[u8]>, AllocError> {222// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously223// allocated with this `Allocator`.224unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags, nid) }225}226}227228// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that229// - memory remains valid until it is explicitly freed,230// - passing a pointer to a valid memory allocation is OK,231// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.232unsafe impl Allocator for KVmalloc {233const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;234235#[inline]236unsafe fn realloc(237ptr: Option<NonNull<u8>>,238layout: Layout,239old_layout: Layout,240flags: Flags,241nid: NumaNode,242) -> Result<NonNull<[u8]>, AllocError> {243// `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc`244// compatible layout.245let layout = Kmalloc::aligned_layout(layout);246247// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously248// allocated with this `Allocator`.249unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags, nid) }250}251}252253#[macros::kunit_tests(rust_allocator)]254mod tests {255use super::*;256use core::mem::MaybeUninit;257use kernel::prelude::*;258259#[test]260fn test_alignment() -> Result {261const TEST_SIZE: usize = 1024;262const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;263264// These two structs are used to test allocating aligned memory.265// they don't need to be accessed, so they're marked as dead_code.266#[expect(dead_code)]267#[repr(align(128))]268struct Blob([u8; TEST_SIZE]);269#[expect(dead_code)]270#[repr(align(8192))]271struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);272273struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);274impl<T, A: Allocator> TestAlign<T, A> {275fn new() -> Result<Self> {276Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))277}278279fn is_aligned_to(&self, align: usize) -> bool {280assert!(align.is_power_of_two());281282let addr = self.0.as_ptr() as usize;283addr & (align - 1) == 0284}285}286287let ta = TestAlign::<Blob, Kmalloc>::new()?;288assert!(ta.is_aligned_to(128));289290let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;291assert!(ta.is_aligned_to(8192));292293let ta = TestAlign::<Blob, Vmalloc>::new()?;294assert!(ta.is_aligned_to(128));295296let ta = TestAlign::<LargeAlignBlob, Vmalloc>::new()?;297assert!(ta.is_aligned_to(8192));298299let ta = TestAlign::<Blob, KVmalloc>::new()?;300assert!(ta.is_aligned_to(128));301302let ta = TestAlign::<LargeAlignBlob, KVmalloc>::new()?;303assert!(ta.is_aligned_to(8192));304305Ok(())306}307}308309310