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