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