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 /// # Invariants 47 /// 48 /// One of the following: `krealloc`, `vrealloc`, `kvrealloc`. 49 struct ReallocFunc( 50 unsafe extern "C" fn(*const crate::ffi::c_void, usize, u32) -> *mut crate::ffi::c_void, 51 ); 52 53 impl ReallocFunc { 54 // INVARIANT: `krealloc` satisfies the type invariants. 55 const KREALLOC: Self = Self(bindings::krealloc); 56 57 // INVARIANT: `vrealloc` satisfies the type invariants. 58 const VREALLOC: Self = Self(bindings::vrealloc); 59 60 // INVARIANT: `kvrealloc` satisfies the type invariants. 61 const KVREALLOC: Self = Self(bindings::kvrealloc); 62 63 /// # Safety 64 /// 65 /// This method has the same safety requirements as [`Allocator::realloc`]. 66 /// 67 /// # Guarantees 68 /// 69 /// This method has the same guarantees as `Allocator::realloc`. Additionally 70 /// - it accepts any pointer to a valid memory allocation allocated by this function. 71 /// - memory allocated by this function remains valid until it is passed to this function. 72 #[inline] call( &self, ptr: Option<NonNull<u8>>, layout: Layout, old_layout: Layout, flags: Flags, ) -> Result<NonNull<[u8]>, AllocError>73 unsafe fn call( 74 &self, 75 ptr: Option<NonNull<u8>>, 76 layout: Layout, 77 old_layout: Layout, 78 flags: Flags, 79 ) -> Result<NonNull<[u8]>, AllocError> { 80 let size = layout.size(); 81 let ptr = match ptr { 82 Some(ptr) => { 83 if old_layout.size() == 0 { 84 ptr::null() 85 } else { 86 ptr.as_ptr() 87 } 88 } 89 None => ptr::null(), 90 }; 91 92 // SAFETY: 93 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that 94 // `ptr` is NULL or valid. 95 // - `ptr` is either NULL or valid by the safety requirements of this function. 96 // 97 // GUARANTEE: 98 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. 99 // - Those functions provide the guarantees of this function. 100 let raw_ptr = unsafe { 101 // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. 102 self.0(ptr.cast(), size, flags.0).cast() 103 }; 104 105 let ptr = if size == 0 { 106 crate::alloc::dangling_from_layout(layout) 107 } else { 108 NonNull::new(raw_ptr).ok_or(AllocError)? 109 }; 110 111 Ok(NonNull::slice_from_raw_parts(ptr, size)) 112 } 113 } 114 115 impl Kmalloc { 116 /// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of 117 /// `layout`. aligned_layout(layout: Layout) -> Layout118 pub fn aligned_layout(layout: Layout) -> Layout { 119 // Note that `layout.size()` (after padding) is guaranteed to be a multiple of 120 // `layout.align()` which together with the slab guarantees means that `Kmalloc` will return 121 // a properly aligned object (see comments in `kmalloc()` for more information). 122 layout.pad_to_align() 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] realloc( ptr: Option<NonNull<u8>>, layout: Layout, old_layout: Layout, flags: Flags, ) -> Result<NonNull<[u8]>, AllocError>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 let layout = Kmalloc::aligned_layout(layout); 139 140 // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. 141 unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) } 142 } 143 } 144 145 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 146 // - memory remains valid until it is explicitly freed, 147 // - passing a pointer to a valid memory allocation is OK, 148 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 149 unsafe impl Allocator for Vmalloc { 150 #[inline] realloc( ptr: Option<NonNull<u8>>, layout: Layout, old_layout: Layout, flags: Flags, ) -> Result<NonNull<[u8]>, AllocError>151 unsafe fn realloc( 152 ptr: Option<NonNull<u8>>, 153 layout: Layout, 154 old_layout: Layout, 155 flags: Flags, 156 ) -> Result<NonNull<[u8]>, AllocError> { 157 // TODO: Support alignments larger than PAGE_SIZE. 158 if layout.align() > bindings::PAGE_SIZE { 159 pr_warn!("Vmalloc does not support alignments larger than PAGE_SIZE yet.\n"); 160 return Err(AllocError); 161 } 162 163 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 164 // allocated with this `Allocator`. 165 unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) } 166 } 167 } 168 169 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 170 // - memory remains valid until it is explicitly freed, 171 // - passing a pointer to a valid memory allocation is OK, 172 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 173 unsafe impl Allocator for KVmalloc { 174 #[inline] realloc( ptr: Option<NonNull<u8>>, layout: Layout, old_layout: Layout, flags: Flags, ) -> Result<NonNull<[u8]>, AllocError>175 unsafe fn realloc( 176 ptr: Option<NonNull<u8>>, 177 layout: Layout, 178 old_layout: Layout, 179 flags: Flags, 180 ) -> Result<NonNull<[u8]>, AllocError> { 181 // `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc` 182 // compatible layout. 183 let layout = Kmalloc::aligned_layout(layout); 184 185 // TODO: Support alignments larger than PAGE_SIZE. 186 if layout.align() > bindings::PAGE_SIZE { 187 pr_warn!("KVmalloc does not support alignments larger than PAGE_SIZE yet.\n"); 188 return Err(AllocError); 189 } 190 191 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 192 // allocated with this `Allocator`. 193 unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) } 194 } 195 } 196