Skip to content

Commit 35efdda

Browse files
alloc: small slab allocator
* Slab allocator for small objects (under 1/8 of page) Signed-off-by: Andy-Python-Programmer <[email protected]>
1 parent 49feaa0 commit 35efdda

File tree

8 files changed

+194
-15
lines changed

8 files changed

+194
-15
lines changed

src/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[workspace]
22
members = ["aero_kernel", "aero_syscall", "aero_proc"]
3+
4+
[profile.release]
5+
debug = true

src/aero_kernel/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ log = "0.4.14"
3333
xmas-elf = "0.8.0"
3434
hashbrown = "0.12.3"
3535
rustc-demangle = "0.1.20"
36-
intrusive-collections = "0.9.2"
36+
# intrusive-collections:
37+
# `nightly`: Get access to const variants of the functions.
38+
intrusive-collections = { version = "0.9.2", features = ["nightly"] }
3739
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
3840
lai = { git = "https://github.com/aero-os/lai-rs" }
3941
uapi = { path = "../uapi" }

src/aero_kernel/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
maybe_uninit_write_slice,
4646
slice_ptr_get,
4747
maybe_uninit_as_bytes,
48-
pointer_is_aligned
48+
pointer_is_aligned,
49+
const_trait_impl,
50+
int_roundings,
51+
const_ptr_is_null
4952
)]
5053
#![deny(trivial_numeric_casts, unused_allocation)]
5154
#![test_runner(crate::tests::test_runner)]

src/aero_kernel/src/mem/alloc.rs

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,56 @@
2020
use core::alloc;
2121
use core::alloc::{GlobalAlloc, Layout};
2222

23-
use super::paging::FRAME_ALLOCATOR;
23+
use super::slab::{SlabHeader, SmallSlab};
2424
use super::vmalloc;
2525
use crate::mem::paging::*;
26+
use crate::utils::sync::IrqGuard;
2627

27-
struct Allocator {}
28+
struct Allocator {
29+
zones: [SmallSlab; 9],
30+
}
2831

2932
impl Allocator {
3033
const fn new() -> Self {
31-
Self {}
34+
Self {
35+
zones: [
36+
SmallSlab::new(8),
37+
SmallSlab::new(16),
38+
SmallSlab::new(32),
39+
SmallSlab::new(64),
40+
SmallSlab::new(128),
41+
SmallSlab::new(256),
42+
SmallSlab::new(512),
43+
SmallSlab::new(1024),
44+
SmallSlab::new(2048),
45+
],
46+
}
3247
}
3348

3449
fn alloc(&self, layout: Layout) -> *mut u8 {
50+
let _guard = IrqGuard::new();
51+
3552
let size = align_up(layout.size() as _, layout.align() as _);
3653

37-
if size <= Size4KiB::SIZE {
38-
let frame: PhysFrame<Size4KiB> = FRAME_ALLOCATOR.allocate_frame().unwrap();
39-
frame.start_address().as_hhdm_virt().as_mut_ptr()
40-
} else if size <= Size2MiB::SIZE {
41-
let frame: PhysFrame<Size2MiB> = FRAME_ALLOCATOR.allocate_frame().unwrap();
42-
frame.start_address().as_hhdm_virt().as_mut_ptr()
54+
for slab in self.zones.iter() {
55+
if size as usize <= slab.size() {
56+
return slab.alloc();
57+
}
58+
}
59+
60+
if size <= Size2MiB::SIZE {
61+
FRAME_ALLOCATOR
62+
.alloc(size as usize)
63+
.unwrap()
64+
.as_hhdm_virt()
65+
.as_mut_ptr()
4366
} else {
4467
let size = align_up(size, Size4KiB::SIZE) / Size4KiB::SIZE;
4568

4669
vmalloc::get_vmalloc()
4770
.alloc(size as usize)
4871
.map(|addr| addr.as_mut_ptr::<u8>())
49-
.unwrap_or(core::ptr::null_mut())
72+
.unwrap()
5073
}
5174
}
5275

@@ -58,7 +81,13 @@ impl Allocator {
5881
return;
5982
}
6083

61-
// TODO: free the slab.
84+
let _guard = IrqGuard::new();
85+
if layout.size() <= 1024 {
86+
let slab_header = (ptr as usize & !(0xfff)) as *mut SlabHeader;
87+
88+
let slab_header = unsafe { &mut *slab_header };
89+
slab_header.ptr.dealloc(ptr);
90+
}
6291
}
6392
}
6493

src/aero_kernel/src/mem/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
pub mod alloc;
2121
pub mod paging;
2222
pub mod pti;
23+
mod slab;
2324
mod vmalloc;
2425

2526
use core::alloc::Layout;

src/aero_kernel/src/mem/slab.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (C) 2021-2023 The Aero Project Developers.
3+
*
4+
* This file is part of The Aero Project.
5+
*
6+
* Aero is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Aero is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Aero. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
use core::ptr::NonNull;
21+
22+
use intrusive_collections::UnsafeRef;
23+
24+
use crate::mem::paging::*;
25+
use crate::utils::sync::Mutex;
26+
27+
#[repr(C)]
28+
pub struct SlabHeader {
29+
pub ptr: UnsafeRef<SmallSlab>,
30+
}
31+
32+
const_assert_eq!(core::mem::size_of::<SlabHeader>(), 8);
33+
34+
/// For small slabs, the [`BufCtl`]s are stored inline.
35+
struct BufCtl(Option<NonNull<BufCtl>>);
36+
37+
impl BufCtl {
38+
const NULL: Self = Self(None);
39+
40+
/// Constructs a [`BufCtl`] from a raw pointer.
41+
const fn from_ptr(ptr: *mut BufCtl) -> Self {
42+
assert!(!ptr.is_null());
43+
44+
// SAFETY: We have verified above that the pointer is non-null.
45+
Self(Some(unsafe { NonNull::new_unchecked(ptr) }))
46+
}
47+
}
48+
49+
const_assert_eq!(core::mem::size_of::<BufCtl>(), 8);
50+
51+
/// Used for allocations smaller than `1/8` of a page.
52+
pub struct SmallSlab {
53+
/// Size of the slab.
54+
size: usize,
55+
first_free: Mutex<BufCtl>,
56+
}
57+
58+
impl SmallSlab {
59+
pub const fn new(size: usize) -> Self {
60+
assert!(size.is_power_of_two());
61+
62+
Self {
63+
size,
64+
first_free: Mutex::new(BufCtl::NULL),
65+
}
66+
}
67+
68+
pub fn alloc(&self) -> *mut u8 {
69+
let mut first_free = self.first_free.lock_irq();
70+
71+
if let Some(entry) = first_free.0 {
72+
*first_free = BufCtl(unsafe { entry.as_ref() }.0);
73+
entry.as_ptr().cast()
74+
} else {
75+
drop(first_free);
76+
77+
self.expand();
78+
self.alloc()
79+
}
80+
}
81+
82+
pub fn dealloc(&self, ptr: *mut u8) {
83+
assert!(!ptr.is_null());
84+
85+
let mut first_free = self.first_free.lock_irq();
86+
87+
let mut new_head = BufCtl::from_ptr(ptr.cast());
88+
new_head.0 = first_free.0;
89+
*first_free = new_head;
90+
}
91+
92+
fn expand(&self) {
93+
let frame: PhysFrame<Size4KiB> = FRAME_ALLOCATOR.allocate_frame().expect("slab: OOM");
94+
95+
let ptr = frame.start_address().as_hhdm_virt().as_mut_ptr::<u8>();
96+
let header_size =
97+
align_up(core::mem::size_of::<SlabHeader>() as u64, self.size as u64) as usize;
98+
99+
let avaliable_size = Size4KiB::SIZE as usize - header_size;
100+
let slab_ptr = unsafe { &mut *ptr.cast::<SlabHeader>() };
101+
102+
// SAFETY: We are constructing an [`UnsafeRef`] from ourselves which is a valid reference.
103+
slab_ptr.ptr = unsafe { UnsafeRef::from_raw(self as *const _) };
104+
105+
let first_free = unsafe { ptr.add(header_size).cast() };
106+
*self.first_free.lock_irq() = BufCtl::from_ptr(first_free);
107+
108+
// Initialize the free-list:
109+
//
110+
// For objects smaller than 1/8 of a page, A slab is built by allocating a 4KiB page,
111+
// placing the slab header at the end, and dividing the rest into equal-size buffers:
112+
//
113+
// ------------------------------------------------------
114+
// | buffer | buffer | buffer | buffer | slab header
115+
// ------------------------------------------------------
116+
// 4KiB
117+
let max = (avaliable_size / self.size) - 1;
118+
let fact = self.size / 8;
119+
120+
for i in 0..max {
121+
unsafe {
122+
let entry = first_free.add(i * fact);
123+
let next = first_free.add((i + 1) * fact);
124+
125+
(&mut *entry).0 = Some(NonNull::new_unchecked(next));
126+
}
127+
}
128+
129+
unsafe {
130+
let entry = &mut *first_free.add(max * fact);
131+
*entry = BufCtl::NULL;
132+
}
133+
}
134+
135+
pub fn size(&self) -> usize {
136+
self.size
137+
}
138+
}
139+
140+
unsafe impl Send for SmallSlab {}
141+
unsafe impl Sync for SmallSlab {}

src/aero_kernel/src/utils/dma.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ unsafe impl Allocator for DmaAllocator {
3737
// XXX: The DMA buffer must be aligned to a page boundary.
3838
let size_bytes = layout.size();
3939

40-
let phys = FRAME_ALLOCATOR.alloc(size_bytes).ok_or(AllocError)?;
40+
let phys = FRAME_ALLOCATOR.alloc_zeroed(size_bytes).ok_or(AllocError)?;
4141
let virt = phys.as_hhdm_virt();
4242

4343
// SAFETY: The frame is aligned and non-null.

0 commit comments

Comments
 (0)