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::{ 12 AllocError, 13 Allocator, 14 Flags, 15 NumaNode, // 16 }; 17 18 use crate::{ 19 bindings, 20 page, // 21 }; 22 23 use core::{ 24 alloc::Layout, 25 ptr::{ 26 self, 27 NonNull, // 28 }, // 29 }; 30 31 const ARCH_KMALLOC_MINALIGN: usize = bindings::ARCH_KMALLOC_MINALIGN; 32 33 mod iter; 34 pub use self::iter::VmallocPageIter; 35 36 /// The contiguous kernel allocator. 37 /// 38 /// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also 39 /// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. 40 /// 41 /// For more details see [self]. 42 pub struct Kmalloc; 43 44 /// The virtually contiguous kernel allocator. 45 /// 46 /// `Vmalloc` allocates pages from the page level allocator and maps them into the contiguous kernel 47 /// virtual space. It is typically used for large allocations. The memory allocated with this 48 /// allocator is not physically contiguous. 49 /// 50 /// For more details see [self]. 51 pub struct Vmalloc; 52 53 /// The kvmalloc kernel allocator. 54 /// 55 /// `KVmalloc` attempts to allocate memory with `Kmalloc` first, but falls back to `Vmalloc` upon 56 /// failure. This allocator is typically used when the size for the requested allocation is not 57 /// known and may exceed the capabilities of `Kmalloc`. 58 /// 59 /// For more details see [self]. 60 pub struct KVmalloc; 61 62 /// # Invariants 63 /// 64 /// One of the following: `krealloc_node_align`, `vrealloc_node_align`, `kvrealloc_node_align`. 65 struct ReallocFunc( 66 unsafe extern "C" fn( 67 *const crate::ffi::c_void, 68 usize, 69 crate::ffi::c_ulong, 70 u32, 71 crate::ffi::c_int, 72 ) -> *mut crate::ffi::c_void, 73 ); 74 75 impl ReallocFunc { 76 // INVARIANT: `krealloc_node_align` satisfies the type invariants. 77 const KREALLOC: Self = Self(bindings::krealloc_node_align); 78 79 // INVARIANT: `vrealloc_node_align` satisfies the type invariants. 80 const VREALLOC: Self = Self(bindings::vrealloc_node_align); 81 82 // INVARIANT: `kvrealloc_node_align` satisfies the type invariants. 83 const KVREALLOC: Self = Self(bindings::kvrealloc_node_align); 84 85 /// # Safety 86 /// 87 /// This method has the same safety requirements as [`Allocator::realloc`]. 88 /// 89 /// # Guarantees 90 /// 91 /// This method has the same guarantees as `Allocator::realloc`. Additionally 92 /// - it accepts any pointer to a valid memory allocation allocated by this function. 93 /// - memory allocated by this function remains valid until it is passed to this function. 94 #[inline] 95 unsafe fn call( 96 &self, 97 ptr: Option<NonNull<u8>>, 98 layout: Layout, 99 old_layout: Layout, 100 flags: Flags, 101 nid: NumaNode, 102 ) -> Result<NonNull<[u8]>, AllocError> { 103 let size = layout.size(); 104 let ptr = match ptr { 105 Some(ptr) => { 106 if old_layout.size() == 0 { 107 ptr::null() 108 } else { 109 ptr.as_ptr() 110 } 111 } 112 None => ptr::null(), 113 }; 114 115 // SAFETY: 116 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that 117 // `ptr` is NULL or valid. 118 // - `ptr` is either NULL or valid by the safety requirements of this function. 119 // 120 // GUARANTEE: 121 // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. 122 // - Those functions provide the guarantees of this function. 123 let raw_ptr = unsafe { 124 // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. 125 self.0(ptr.cast(), size, layout.align(), flags.0, nid.0).cast() 126 }; 127 128 let ptr = if size == 0 { 129 crate::alloc::dangling_from_layout(layout) 130 } else { 131 NonNull::new(raw_ptr).ok_or(AllocError)? 132 }; 133 134 Ok(NonNull::slice_from_raw_parts(ptr, size)) 135 } 136 } 137 138 impl Kmalloc { 139 /// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of 140 /// `layout`. 141 pub fn aligned_layout(layout: Layout) -> Layout { 142 // Note that `layout.size()` (after padding) is guaranteed to be a multiple of 143 // `layout.align()` which together with the slab guarantees means that `Kmalloc` will return 144 // a properly aligned object (see comments in `kmalloc()` for more information). 145 layout.pad_to_align() 146 } 147 } 148 149 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 150 // - memory remains valid until it is explicitly freed, 151 // - passing a pointer to a valid memory allocation is OK, 152 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 153 unsafe impl Allocator for Kmalloc { 154 const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN; 155 156 #[inline] 157 unsafe fn realloc( 158 ptr: Option<NonNull<u8>>, 159 layout: Layout, 160 old_layout: Layout, 161 flags: Flags, 162 nid: NumaNode, 163 ) -> Result<NonNull<[u8]>, AllocError> { 164 let layout = Kmalloc::aligned_layout(layout); 165 166 // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. 167 unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags, nid) } 168 } 169 } 170 171 impl Vmalloc { 172 /// Convert a pointer to a [`Vmalloc`] allocation to a [`page::BorrowedPage`]. 173 /// 174 /// # Examples 175 /// 176 /// ``` 177 /// # use core::ptr::{NonNull, from_mut}; 178 /// # use kernel::{page, prelude::*}; 179 /// use kernel::alloc::allocator::Vmalloc; 180 /// 181 /// let mut vbox = VBox::<[u8; page::PAGE_SIZE]>::new_uninit(GFP_KERNEL)?; 182 /// 183 /// { 184 /// // SAFETY: By the type invariant of `Box` the inner pointer of `vbox` is non-null. 185 /// let ptr = unsafe { NonNull::new_unchecked(from_mut(&mut *vbox)) }; 186 /// 187 /// // SAFETY: 188 /// // `ptr` is a valid pointer to a `Vmalloc` allocation. 189 /// // `ptr` is valid for the entire lifetime of `page`. 190 /// let page = unsafe { Vmalloc::to_page(ptr.cast()) }; 191 /// 192 /// // SAFETY: There is no concurrent read or write to the same page. 193 /// unsafe { page.fill_zero_raw(0, page::PAGE_SIZE)? }; 194 /// } 195 /// # Ok::<(), Error>(()) 196 /// ``` 197 /// 198 /// # Safety 199 /// 200 /// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation. 201 /// - `ptr` must remain valid for the entire duration of `'a`. 202 pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> { 203 // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory. 204 let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) }; 205 206 // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer 207 // to `Vmalloc` memory. 208 let page = unsafe { NonNull::new_unchecked(page) }; 209 210 // SAFETY: 211 // - `page` is a valid pointer to a `struct page`, given that by the safety requirements of 212 // this function `ptr` is a valid pointer to a `Vmalloc` allocation. 213 // - By the safety requirements of this function `ptr` is valid for the entire lifetime of 214 // `'a`. 215 unsafe { page::BorrowedPage::from_raw(page) } 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 Vmalloc { 224 const MIN_ALIGN: usize = kernel::page::PAGE_SIZE; 225 226 #[inline] 227 unsafe fn realloc( 228 ptr: Option<NonNull<u8>>, 229 layout: Layout, 230 old_layout: Layout, 231 flags: Flags, 232 nid: NumaNode, 233 ) -> Result<NonNull<[u8]>, AllocError> { 234 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 235 // allocated with this `Allocator`. 236 unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags, nid) } 237 } 238 } 239 240 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that 241 // - memory remains valid until it is explicitly freed, 242 // - passing a pointer to a valid memory allocation is OK, 243 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. 244 unsafe impl Allocator for KVmalloc { 245 const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN; 246 247 #[inline] 248 unsafe fn realloc( 249 ptr: Option<NonNull<u8>>, 250 layout: Layout, 251 old_layout: Layout, 252 flags: Flags, 253 nid: NumaNode, 254 ) -> Result<NonNull<[u8]>, AllocError> { 255 // `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc` 256 // compatible layout. 257 let layout = Kmalloc::aligned_layout(layout); 258 259 // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously 260 // allocated with this `Allocator`. 261 unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags, nid) } 262 } 263 } 264 265 #[macros::kunit_tests(rust_allocator)] 266 mod tests { 267 use super::*; 268 use core::mem::MaybeUninit; 269 use kernel::prelude::*; 270 271 #[test] 272 fn test_alignment() -> Result { 273 const TEST_SIZE: usize = 1024; 274 const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4; 275 276 // These two structs are used to test allocating aligned memory. 277 // they don't need to be accessed, so they're marked as dead_code. 278 #[expect(dead_code)] 279 #[repr(align(128))] 280 struct Blob([u8; TEST_SIZE]); 281 #[expect(dead_code)] 282 #[repr(align(8192))] 283 struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]); 284 285 struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>); 286 impl<T, A: Allocator> TestAlign<T, A> { 287 fn new() -> Result<Self> { 288 Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?)) 289 } 290 291 fn is_aligned_to(&self, align: usize) -> bool { 292 assert!(align.is_power_of_two()); 293 294 let addr = self.0.as_ptr() as usize; 295 addr & (align - 1) == 0 296 } 297 } 298 299 let ta = TestAlign::<Blob, Kmalloc>::new()?; 300 assert!(ta.is_aligned_to(128)); 301 302 let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?; 303 assert!(ta.is_aligned_to(8192)); 304 305 let ta = TestAlign::<Blob, Vmalloc>::new()?; 306 assert!(ta.is_aligned_to(128)); 307 308 let ta = TestAlign::<LargeAlignBlob, Vmalloc>::new()?; 309 assert!(ta.is_aligned_to(8192)); 310 311 let ta = TestAlign::<Blob, KVmalloc>::new()?; 312 assert!(ta.is_aligned_to(128)); 313 314 let ta = TestAlign::<LargeAlignBlob, KVmalloc>::new()?; 315 assert!(ta.is_aligned_to(8192)); 316 317 Ok(()) 318 } 319 } 320