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