Skip to content

Commit 29c01de

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 <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent da2b933 commit 29c01de

File tree

1 file changed

+171
-7
lines changed

1 file changed

+171
-7
lines changed

rust/kernel/alloc/allocator_test.rs

Lines changed: 171 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,184 @@
22

33
#![allow(missing_docs)]
44

5-
use super::{AllocError, Allocator, Flags};
5+
use super::{flags::*, AllocError, Allocator, Flags};
66
use core::alloc::Layout;
7+
use core::cmp;
8+
use core::mem;
9+
use core::ptr;
710
use core::ptr::NonNull;
811

9-
pub struct Kmalloc;
12+
pub struct Cmalloc;
13+
pub type Kmalloc = Cmalloc;
1014
pub type Vmalloc = Kmalloc;
1115
pub type KVmalloc = Kmalloc;
1216

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

0 commit comments

Comments
 (0)