xref: /linux/rust/kernel/alloc/allocator.rs (revision 6b3f7af57881f6d6250c6dcc4d910fe8e855a607)
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::{
178     /// #     from_mut,
179     /// #     NonNull, //
180     /// # };
181     /// # use kernel::page;
182     /// use kernel::alloc::allocator::Vmalloc;
183     ///
184     /// let mut vbox = VBox::<[u8; page::PAGE_SIZE]>::new_uninit(GFP_KERNEL)?;
185     ///
186     /// {
187     ///     // SAFETY: By the type invariant of `Box` the inner pointer of `vbox` is non-null.
188     ///     let ptr = unsafe { NonNull::new_unchecked(from_mut(&mut *vbox)) };
189     ///
190     ///     // SAFETY:
191     ///     // `ptr` is a valid pointer to a `Vmalloc` allocation.
192     ///     // `ptr` is valid for the entire lifetime of `page`.
193     ///     let page = unsafe { Vmalloc::to_page(ptr.cast()) };
194     ///
195     ///     // SAFETY: There is no concurrent read or write to the same page.
196     ///     unsafe { page.fill_zero_raw(0, page::PAGE_SIZE)? };
197     /// }
198     /// # Ok::<(), Error>(())
199     /// ```
200     ///
201     /// # Safety
202     ///
203     /// - `ptr` must be a valid pointer to a [`Vmalloc`] allocation.
204     /// - `ptr` must remain valid for the entire duration of `'a`.
205     pub unsafe fn to_page<'a>(ptr: NonNull<u8>) -> page::BorrowedPage<'a> {
206         // SAFETY: `ptr` is a valid pointer to `Vmalloc` memory.
207         let page = unsafe { bindings::vmalloc_to_page(ptr.as_ptr().cast()) };
208 
209         // SAFETY: `vmalloc_to_page` returns a valid pointer to a `struct page` for a valid pointer
210         // to `Vmalloc` memory.
211         let page = unsafe { NonNull::new_unchecked(page) };
212 
213         // SAFETY:
214         // - `page` is a valid pointer to a `struct page`, given that by the safety requirements of
215         //   this function `ptr` is a valid pointer to a `Vmalloc` allocation.
216         // - By the safety requirements of this function `ptr` is valid for the entire lifetime of
217         //   `'a`.
218         unsafe { page::BorrowedPage::from_raw(page) }
219     }
220 }
221 
222 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
223 // - memory remains valid until it is explicitly freed,
224 // - passing a pointer to a valid memory allocation is OK,
225 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
226 unsafe impl Allocator for Vmalloc {
227     const MIN_ALIGN: usize = kernel::page::PAGE_SIZE;
228 
229     #[inline]
230     unsafe fn realloc(
231         ptr: Option<NonNull<u8>>,
232         layout: Layout,
233         old_layout: Layout,
234         flags: Flags,
235         nid: NumaNode,
236     ) -> Result<NonNull<[u8]>, AllocError> {
237         // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
238         // allocated with this `Allocator`.
239         unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags, nid) }
240     }
241 }
242 
243 // SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
244 // - memory remains valid until it is explicitly freed,
245 // - passing a pointer to a valid memory allocation is OK,
246 // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
247 unsafe impl Allocator for KVmalloc {
248     const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;
249 
250     #[inline]
251     unsafe fn realloc(
252         ptr: Option<NonNull<u8>>,
253         layout: Layout,
254         old_layout: Layout,
255         flags: Flags,
256         nid: NumaNode,
257     ) -> Result<NonNull<[u8]>, AllocError> {
258         // `KVmalloc` may use the `Kmalloc` backend, hence we have to enforce a `Kmalloc`
259         // compatible layout.
260         let layout = Kmalloc::aligned_layout(layout);
261 
262         // SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
263         // allocated with this `Allocator`.
264         unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags, nid) }
265     }
266 }
267 
268 #[cfg(CONFIG_RUST_ALLOCATOR_KUNIT_TEST)]
269 #[macros::kunit_tests(rust_allocator)]
270 mod tests {
271     use super::*;
272     use core::mem::MaybeUninit;
273     use kernel::prelude::*;
274 
275     #[test]
276     fn test_alignment() -> Result {
277         const TEST_SIZE: usize = 1024;
278         const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
279 
280         // These two structs are used to test allocating aligned memory.
281         // they don't need to be accessed, so they're marked as dead_code.
282         #[expect(dead_code)]
283         #[repr(align(128))]
284         struct Blob([u8; TEST_SIZE]);
285         #[expect(dead_code)]
286         #[repr(align(8192))]
287         struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
288 
289         struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
290         impl<T, A: Allocator> TestAlign<T, A> {
291             fn new() -> Result<Self> {
292                 Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
293             }
294 
295             fn is_aligned_to(&self, align: usize) -> bool {
296                 assert!(align.is_power_of_two());
297 
298                 let addr = self.0.as_ptr() as usize;
299                 addr & (align - 1) == 0
300             }
301         }
302 
303         let ta = TestAlign::<Blob, Kmalloc>::new()?;
304         assert!(ta.is_aligned_to(128));
305 
306         let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
307         assert!(ta.is_aligned_to(8192));
308 
309         let ta = TestAlign::<Blob, Vmalloc>::new()?;
310         assert!(ta.is_aligned_to(128));
311 
312         let ta = TestAlign::<LargeAlignBlob, Vmalloc>::new()?;
313         assert!(ta.is_aligned_to(8192));
314 
315         let ta = TestAlign::<Blob, KVmalloc>::new()?;
316         assert!(ta.is_aligned_to(128));
317 
318         let ta = TestAlign::<LargeAlignBlob, KVmalloc>::new()?;
319         assert!(ta.is_aligned_to(8192));
320 
321         Ok(())
322     }
323 }
324