Skip to content

Commit 2963c6e

Browse files
Danilo Krummrichfbq
authored andcommitted
rust: alloc: implement Allocator for Kmalloc
Implement `Allocator` for `Kmalloc`, the kernel's default allocator, typically used for objects smaller than page size. All memory allocations made with `Kmalloc` end up in `krealloc()`. It serves as allocator for the subsequently introduced types `KBox` and `KVec`. Signed-off-by: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/r/20240911225449.152928-5-dakr@kernel.org
1 parent 008c963 commit 2963c6e

2 files changed

Lines changed: 89 additions & 2 deletions

File tree

rust/kernel/alloc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#[cfg(not(test))]
66
#[cfg(not(testlib))]
7-
mod allocator;
7+
pub mod allocator;
88
pub mod box_ext;
99
pub mod vec_ext;
1010

rust/kernel/alloc/allocator.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
//! 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>
410
511
use super::{flags::*, Flags};
612
use core::alloc::{GlobalAlloc, Layout};
713
use core::ptr;
14+
use core::ptr::NonNull;
815

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;
1026

1127
/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
1228
///
@@ -31,6 +47,77 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
3147
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
3248
}
3349

50+
/// # Invariants
51+
///
52+
/// One of the following `krealloc`, `vrealloc`, `kvrealloc`.
53+
struct ReallocFunc(
54+
unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void,
55+
);
56+
57+
impl ReallocFunc {
58+
// INVARIANT: `krealloc` satisfies the type invariants.
59+
const KREALLOC: Self = Self(bindings::krealloc);
60+
61+
/// # Safety
62+
///
63+
/// This method has the same safety requirements as [`Allocator::realloc`].
64+
///
65+
/// # Guarantees
66+
///
67+
/// This method has the same guarantees as `Allocator::realloc`. Additionally
68+
/// - it accepts any pointer to a valid memory allocation allocated by this function.
69+
/// - memory allocated by this function remains valid until it is passed to this function.
70+
unsafe fn call(
71+
&self,
72+
ptr: Option<NonNull<u8>>,
73+
layout: Layout,
74+
flags: Flags,
75+
) -> Result<NonNull<[u8]>, AllocError> {
76+
let size = aligned_size(layout);
77+
let ptr = match ptr {
78+
Some(ptr) => ptr.as_ptr(),
79+
None => ptr::null(),
80+
};
81+
82+
// SAFETY:
83+
// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that
84+
// `ptr` is NULL or valid.
85+
// - `ptr` is either NULL or valid by the safety requirements of this function.
86+
//
87+
// GUARANTEE:
88+
// - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`.
89+
// - Those functions provide the guarantees of this function.
90+
let raw_ptr = unsafe {
91+
// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
92+
self.0(ptr.cast(), size, flags.0).cast()
93+
};
94+
95+
let ptr = if size == 0 {
96+
NonNull::dangling()
97+
} else {
98+
NonNull::new(raw_ptr).ok_or(AllocError)?
99+
};
100+
101+
Ok(NonNull::slice_from_raw_parts(ptr, size))
102+
}
103+
}
104+
105+
// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that
106+
// - memory remains valid until it is explicitly freed,
107+
// - passing a pointer to a valid memory allocation is OK,
108+
// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
109+
unsafe impl Allocator for Kmalloc {
110+
#[inline]
111+
unsafe fn realloc(
112+
ptr: Option<NonNull<u8>>,
113+
layout: Layout,
114+
flags: Flags,
115+
) -> Result<NonNull<[u8]>, AllocError> {
116+
// SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
117+
unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) }
118+
}
119+
}
120+
34121
unsafe impl GlobalAlloc for Kmalloc {
35122
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
36123
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety

0 commit comments

Comments
 (0)