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