1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Allocator support. 4 //! 5 //! Documentation for the kernel's memory allocators can found in the "Memory Allocation Guide" 6 //! linked below. For instance, this includes the concept of "get free page" (GFP) flags and the 7 //! typical application of the different kernel allocators. 8 //! 9 //! Reference: <https://docs.kernel.org/core-api/memory-allocation.html> 10 11 use super::Flags; 12 use core::alloc::Layout; 13 use core::ptr; 14 use core::ptr::NonNull; 15 16 use crate::alloc::{AllocError, Allocator}; 17 use crate::bindings; 18 use crate::pr_warn; 19 20 const ARCH_KMALLOC_MINALIGN: usize = bindings::ARCH_KMALLOC_MINALIGN; 21 22 /// The contiguous kernel allocator. 23 /// 24 /// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also 25 /// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. 26 /// 27 /// For more details see [self]. 28 pub struct Kmalloc; 29 30 /// The virtually contiguous kernel allocator. 31 /// 32 /// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel 33 /// virtual space. It is typically used for large allocations. The memory allocated with this 34 /// allocator is not physically contiguous. 35 /// 36 /// For more details see [self]. 37 pub struct Vmalloc; 38 39 /// The kvmalloc kernel allocator. 40 /// 41 /// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon 42 /// failure. This allocator is typically used when the size for the requested allocation is not 43 /// known and may exceed the capabilities of `Kmalloc`. 44 /// 45 /// For more details see [self]. 46 pub struct KVmalloc; 47 48 /// # Invariants 49 /// 50 /// One of the following: `krealloc`, `vrealloc`, `kvrealloc`. 51 struct ReallocFunc( 52 unsafe extern "C" fn(*const crate::ffi::c_void, usize, u32) -> *mut crate::ffi::c_void, 53 ); 54 55 impl ReallocFunc { 56 // INVARIANT: `krealloc` satisfies the type invariants. 57 const KREALLOC: Self = Self(bindings::krealloc); 58 59 // INVARIANT: `vrealloc` satisfies the type invariants. 60 const VREALLOC: Self = Self(bindings::vrealloc); 61 62 // INVARIANT: `kvrealloc` satisfies the type invariants. 63 const KVREALLOC: Self = Self(bindings::kvrealloc); 64 65 /// # Safety 66 /// 67 /// This method has the same safety requirements as [`Allocator::realloc`]. 68 /// 69 /// # Guarantees 70 /// 71 /// This method has the same guarantees as `Allocator::realloc`. Additionally 72 /// - it accepts any pointer to a valid memory allocation allocated by this function. 73 /// - memory allocated by this function remains valid until it is passed to this function. 74 #[inline] 75 unsafe fn call( 76 &self, 77 ptr: Option<NonNull<u8>>, 78 layout: Layout, 79 old_layout: Layout, 80 flags: Flags, 81 ) -> Result<NonNull<[u8]>, AllocError> { 82 let size = layout.size(); 83 let ptr = match ptr { 84 Some(ptr) => { 85 if old_layout.size() == 0 { 86 ptr::null() 87 } else { 88 ptr.as_ptr() 89 } 90 } 91 None => ptr::null(), 92 }; 93 94 // SAFETY: 95 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that 96 // `ptr` is NULL or valid. 97 // - `ptr` is either NULL or valid by the safety requirements of this function. 98 // 99 // GUARANTEE: 100 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. 101 // - Those functions provide the guarantees of this function. 102 let raw_ptr = unsafe { 103 // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. 104 self.0(ptr.cast(), size, flags.0).cast() 105 }; 106 107 let ptr = if size == 0 { 108 crate::alloc::dangling_from_layout(layout) 109 } else { 110 NonNull::new(raw_ptr).ok_or(AllocError)? 111 }; 112 113 Ok(NonNull::slice_from_raw_parts(ptr, size)) 114 } 115 } 116 117 impl Kmalloc { 118 /// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of 119 /// `layout`. 120 pub fn aligned_layout(layout: Layout) -> Layout { 121 // Note that `layout.size()` (after padding) is guaranteed to be a multiple of 122 // `layout.align()` which together with the slab guarantees means that `Kmalloc` will return 123 // a properly aligned object (see comments in `kmalloc()` for more information). 124 layout.pad_to_align() 125 } 126 } 127 128 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 129 // - memory remains valid until it is explicitly freed, 130 // - passing a pointer to a valid memory allocation is OK, 131 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 132 unsafe impl Allocator for Kmalloc { 133 const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN; 134 135 #[inline] 136 unsafe fn realloc( 137 ptr: Option<NonNull<u8>>, 138 layout: Layout, 139 old_layout: Layout, 140 flags: Flags, 141 ) -> Result<NonNull<[u8]>, AllocError> { 142 let layout = Kmalloc::aligned_layout(layout); 143 144 // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. 145 unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) } 146 } 147 } 148 149 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 150 // - memory remains valid until it is explicitly freed, 151 // - passing a pointer to a valid memory allocation is OK, 152 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 153 unsafe impl Allocator for Vmalloc { 154 const MIN_ALIGN: usize = kernel::page::PAGE_SIZE; 155 156 #[inline] 157 unsafe fn realloc( 158 ptr: Option<NonNull<u8>>, 159 layout: Layout, 160 old_layout: Layout, 161 flags: Flags, 162 ) -> Result<NonNull<[u8]>, AllocError> { 163 // TODO: Support alignments larger than PAGE_SIZE. 164 if layout.align() > bindings::PAGE_SIZE { 165 pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n"); 166 return Err(AllocError); 167 } 168 169 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 170 // allocated with this `Allocator`. 171 unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) } 172 } 173 } 174 175 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 176 // - memory remains valid until it is explicitly freed, 177 // - passing a pointer to a valid memory allocation is OK, 178 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 179 unsafe impl Allocator for KVmalloc { 180 const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN; 181 182 #[inline] 183 unsafe fn realloc( 184 ptr: Option<NonNull<u8>>, 185 layout: Layout, 186 old_layout: Layout, 187 flags: Flags, 188 ) -> Result<NonNull<[u8]>, AllocError> { 189 // `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc` 190 // compatible layout. 191 let layout = Kmalloc::aligned_layout(layout); 192 193 // TODO: Support alignments larger than PAGE_SIZE. 194 if layout.align() > bindings::PAGE_SIZE { 195 pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n"); 196 return Err(AllocError); 197 } 198 199 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 200 // allocated with this `Allocator`. 201 unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) } 202 } 203 } 204