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