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