|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 |
|
3 | 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> |
4 | 10 |
|
5 | 11 | use super::{flags::*, Flags};
|
6 | 12 | use core::alloc::{GlobalAlloc, Layout};
|
7 | 13 | use core::ptr;
|
| 14 | +use core::ptr::NonNull; |
8 | 15 |
|
9 |
| -struct Kmalloc; |
| 16 | +use crate::alloc::{AllocError, Allocator}; |
| 17 | +use crate::bindings; |
| 18 | + |
| 19 | +/// The contiguous kernel allocator. |
| 20 | +/// |
| 21 | +/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also |
| 22 | +/// supports larger allocations up to `bindings::KMALLOC_MAX_SIZE`, which is hardware specific. |
| 23 | +/// |
| 24 | +/// For more details see [self]. |
| 25 | +pub struct Kmalloc; |
10 | 26 |
|
11 | 27 | /// Returns a proper size to alloc a new object aligned to `new_layout`'s alignment.
|
12 | 28 | fn aligned_size(new_layout: Layout) -> usize {
|
@@ -36,6 +52,60 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
|
36 | 52 | unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
|
37 | 53 | }
|
38 | 54 |
|
| 55 | +/// # Invariants |
| 56 | +/// |
| 57 | +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. |
| 58 | +struct ReallocFunc( |
| 59 | + unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void, |
| 60 | +); |
| 61 | + |
| 62 | +impl ReallocFunc { |
| 63 | + // INVARIANT: `krealloc` satisfies the type invariants. |
| 64 | + const KREALLOC: Self = Self(bindings::krealloc); |
| 65 | + |
| 66 | + /// # Safety |
| 67 | + /// |
| 68 | + /// This method has the same safety requirements as [`Allocator::realloc`]. |
| 69 | + unsafe fn call( |
| 70 | + &self, |
| 71 | + ptr: Option<NonNull<u8>>, |
| 72 | + layout: Layout, |
| 73 | + flags: Flags, |
| 74 | + ) -> Result<NonNull<[u8]>, AllocError> { |
| 75 | + let size = aligned_size(layout); |
| 76 | + let ptr = match ptr { |
| 77 | + Some(ptr) => ptr.as_ptr(), |
| 78 | + None => ptr::null(), |
| 79 | + }; |
| 80 | + |
| 81 | + // SAFETY: `ptr` is either NULL or valid by the safety requirements of this function. |
| 82 | + let raw_ptr = unsafe { |
| 83 | + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. |
| 84 | + self.0(ptr.cast(), size, flags.0).cast() |
| 85 | + }; |
| 86 | + |
| 87 | + let ptr = if size == 0 { |
| 88 | + NonNull::dangling() |
| 89 | + } else { |
| 90 | + NonNull::new(raw_ptr).ok_or(AllocError)? |
| 91 | + }; |
| 92 | + |
| 93 | + Ok(NonNull::slice_from_raw_parts(ptr, size)) |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +unsafe impl Allocator for Kmalloc { |
| 98 | + #[inline] |
| 99 | + unsafe fn realloc( |
| 100 | + ptr: Option<NonNull<u8>>, |
| 101 | + layout: Layout, |
| 102 | + flags: Flags, |
| 103 | + ) -> Result<NonNull<[u8]>, AllocError> { |
| 104 | + // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. |
| 105 | + unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) } |
| 106 | + } |
| 107 | +} |
| 108 | + |
39 | 109 | unsafe impl GlobalAlloc for Kmalloc {
|
40 | 110 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
41 | 111 | // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
|
|
0 commit comments