xref: /linux/rust/kernel/alloc/allocator.rs (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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