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