Skip to content

Commit 2412a06

Browse files
Danilo Krummrichfbq
authored andcommitted
rust: alloc: implement Cmalloc in module allocator_test
So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users of those types (e.g. `CString`) use kernel allocators for instantiation. In order to allow userspace test cases to make use of such types as well, implement the `Cmalloc` allocator within the allocator_test module and type alias all kernel allocators to `Cmalloc`. The `Cmalloc` allocator uses libc's realloc() function as allocator backend. Signed-off-by: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/r/20240911225449.152928-23-dakr@kernel.org
1 parent 050c1b0 commit 2412a06

File tree

1 file changed

+186
-7
lines changed

1 file changed

+186
-7
lines changed
Lines changed: 186 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,200 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3+
//! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users
4+
//! of those types (e.g. `CString`) use kernel allocators for instantiation.
5+
//!
6+
//! In order to allow userspace test cases to make use of such types as well, implement the
7+
//! `Cmalloc` allocator within the allocator_test module and type alias all kernel allocators to
8+
//! `Cmalloc`. The `Cmalloc` allocator uses libc's realloc() function as allocator backend.
9+
310
#![allow(missing_docs)]
411

5-
use super::{AllocError, Allocator, Flags};
12+
use super::{flags::*, AllocError, Allocator, Flags};
613
use core::alloc::Layout;
14+
use core::cmp;
15+
use core::mem;
16+
use core::ptr;
717
use core::ptr::NonNull;
818

9-
pub struct Kmalloc;
19+
/// The userspace allocator based on libc.
20+
pub struct Cmalloc;
21+
22+
pub type Kmalloc = Cmalloc;
1023
pub type Vmalloc = Kmalloc;
1124
pub type KVmalloc = Kmalloc;
1225

13-
unsafe impl Allocator for Kmalloc {
26+
extern "C" {
27+
#[link_name = "aligned_alloc"]
28+
fn libc_aligned_alloc(align: usize, size: usize) -> *mut core::ffi::c_void;
29+
30+
#[link_name = "free"]
31+
fn libc_free(ptr: *mut core::ffi::c_void);
32+
}
33+
34+
struct CmallocData {
35+
// The actual size as requested through `Cmalloc::alloc` or `Cmalloc::realloc`.
36+
size: usize,
37+
// The offset from the pointer returned to the caller of `Cmalloc::alloc` or `Cmalloc::realloc`
38+
// to the actual base address of the allocation.
39+
offset: usize,
40+
}
41+
42+
impl Cmalloc {
43+
/// Adjust the size and alignment such that we can additionally store `CmallocData` right
44+
/// before the actual data described by `layout`.
45+
///
46+
/// Example:
47+
///
48+
/// - For `CmallocData` assume an alignment of 8 and a size of 16.
49+
/// - For `layout` assume and alignment of 16 and a size of 64.
50+
///
51+
///```text
52+
/// 0 16 32 96
53+
/// |----------------|----------------|------------------------------------------------|
54+
/// empty CmallocData data
55+
///```
56+
///
57+
/// For this example the returned `Layout` has an alignment of 32 and a size of 96.
58+
fn layout_adjust(layout: Layout) -> Result<Layout, AllocError> {
59+
let layout = layout.pad_to_align();
60+
61+
// Ensure that `CmallocData` fits into half the alignment. Additionally, this guarantees
62+
// that advancing a pointer aligned to `align` by `align / 2` we still satisfy or exceed
63+
// the alignment requested through `layout`.
64+
let align = cmp::max(
65+
layout.align(),
66+
mem::size_of::<CmallocData>().next_power_of_two(),
67+
) * 2;
68+
69+
// Add the additional space required for `CmallocData`.
70+
let size = layout.size() + mem::size_of::<CmallocData>();
71+
72+
Ok(Layout::from_size_align(size, align)
73+
.map_err(|_| AllocError)?
74+
.pad_to_align())
75+
}
76+
77+
fn alloc_store_data(layout: Layout) -> Result<NonNull<u8>, AllocError> {
78+
let requested_size = layout.size();
79+
80+
let layout = Self::layout_adjust(layout)?;
81+
let min_align = layout.align() / 2;
82+
83+
// SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
84+
// exceeds the given size and alignment requirements.
85+
let raw_ptr = unsafe { libc_aligned_alloc(layout.align(), layout.size()) } as *mut u8;
86+
87+
let priv_ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
88+
89+
// SAFETY:
90+
// - By adding `min_align` the pointer remains within the allocated object and `min_align`
91+
// can not exceed `isize`.
92+
//
93+
// GUARANTEE:
94+
// - The adjustments from `Self::layout_adjust` ensure that after this operation the
95+
// original size and alignment requirements are still satisfied or exceeded.
96+
let ptr = unsafe { priv_ptr.as_ptr().add(min_align) };
97+
98+
// SAFETY: `min_align` is greater than or equal to the size of `CmallocData`, hence we
99+
// don't exceed the allocation boundaries.
100+
let data_ptr: *mut CmallocData = unsafe { ptr.sub(mem::size_of::<CmallocData>()) }.cast();
101+
102+
let data = CmallocData {
103+
size: requested_size,
104+
offset: min_align,
105+
};
106+
107+
// SAFETY: `data_ptr` is properly aligned and within the allocation boundaries reserved for
108+
// `CmallocData`.
109+
unsafe { data_ptr.write(data) };
110+
111+
NonNull::new(ptr).ok_or(AllocError)
112+
}
113+
114+
/// # Safety
115+
///
116+
/// - `ptr` must have been previously allocated with `Self::alloc_store_data`.
117+
unsafe fn data(ptr: NonNull<u8>) -> CmallocData {
118+
// SAFETY: `Self::alloc_store_data` stores the `CmallocData` right before the address
119+
// returned to callers of `Self::alloc_store_data`.
120+
let data_ptr: *mut CmallocData =
121+
unsafe { ptr.as_ptr().sub(mem::size_of::<CmallocData>()) }.cast();
122+
123+
// `CmallocData` has been previously stored at this offset with `Self::alloc_store_data`.
124+
//
125+
// SAFETY:
126+
// - `data_ptr` points to a properly aligned and initialized value of `CmallocData`.
127+
unsafe { core::ptr::read(data_ptr) }
128+
}
129+
130+
/// # Safety
131+
///
132+
/// This function must not be called more than once for the same allocation.
133+
///
134+
/// - `ptr` must have been previously allocated with `Self::alloc_store_data`.
135+
unsafe fn free_read_data(ptr: NonNull<u8>) {
136+
// SAFETY: `ptr` has been created by `Self::alloc_store_data`.
137+
let data = unsafe { Self::data(ptr) };
138+
139+
// SAFETY: `ptr` has been created by `Self::alloc_store_data`.
140+
let priv_ptr = unsafe { ptr.as_ptr().sub(data.offset) };
141+
142+
// SAFETY: `priv_ptr` has previously been allocatored with this `Allocator`.
143+
unsafe { libc_free(priv_ptr.cast()) };
144+
}
145+
}
146+
147+
unsafe impl Allocator for Cmalloc {
148+
fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
149+
if layout.size() == 0 {
150+
return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
151+
}
152+
153+
let ptr = Self::alloc_store_data(layout)?;
154+
155+
if flags.contains(__GFP_ZERO) {
156+
// SAFETY: `Self::alloc_store_data` guarantees that `ptr` points to memory of at least
157+
// `layout.size()` bytes.
158+
unsafe { ptr.as_ptr().write_bytes(0, layout.size()) };
159+
}
160+
161+
Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
162+
}
163+
14164
unsafe fn realloc(
15-
_ptr: Option<NonNull<u8>>,
16-
_layout: Layout,
17-
_flags: Flags,
165+
ptr: Option<NonNull<u8>>,
166+
layout: Layout,
167+
flags: Flags,
18168
) -> Result<NonNull<[u8]>, AllocError> {
19-
panic!();
169+
let Some(src) = ptr else {
170+
return Self::alloc(layout, flags);
171+
};
172+
173+
if layout.size() == 0 {
174+
// SAFETY: `src` has been created by `Self::alloc_store_data`.
175+
unsafe { Self::free_read_data(src) };
176+
177+
return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0));
178+
}
179+
180+
let dst = Self::alloc(layout, flags)?;
181+
182+
// SAFETY: `src` has been created by `Self::alloc_store_data`.
183+
let data = unsafe { Self::data(src) };
184+
185+
// SAFETY: `src` has previously been allocated with this `Allocator`; `dst` has just been
186+
// newly allocated. Copy up to the smaller of both sizes.
187+
unsafe {
188+
ptr::copy_nonoverlapping(
189+
src.as_ptr(),
190+
dst.as_ptr().cast(),
191+
cmp::min(layout.size(), data.size),
192+
)
193+
};
194+
195+
// SAFETY: `src` has been created by `Self::alloc_store_data`.
196+
unsafe { Self::free_read_data(src) };
197+
198+
Ok(dst)
20199
}
21200
}

0 commit comments

Comments
 (0)