buddy_slab_allocator/slab/
mod.rs1pub mod cache;
10pub mod page;
11pub mod size_class;
12
13use core::{alloc::Layout, ptr::NonNull};
14
15use ax_sync::{RawSpinLockGuard, SpinLock};
16use cache::{CacheDeallocResult, SlabCache};
17pub use page::SlabPageHeader;
18pub use size_class::SizeClass;
19
20use crate::error::{AllocError, AllocResult};
21
22pub enum SlabAllocResult {
24 Allocated(NonNull<u8>),
26 NeedsSlab { size_class: SizeClass, pages: usize },
30}
31
32pub enum SlabDeallocResult {
34 Done,
36 FreeSlab { base: usize, pages: usize },
38}
39
40pub enum SlabPoolDeallocResult {
42 Done,
44 RemoteQueued,
46 FreeSlab { base: usize, pages: usize },
48}
49
50pub trait SlabTrait: Sync {
52 fn cpu_id(&self) -> usize;
54
55 fn page_size(&self) -> usize;
57
58 fn alloc(&self, layout: Layout) -> AllocResult<SlabAllocResult>;
60
61 fn add_slab(&self, size_class: SizeClass, base: usize, bytes: usize);
63
64 fn dealloc_local(&self, ptr: NonNull<u8>, layout: Layout) -> SlabDeallocResult;
66
67 fn dealloc_remote(&self, ptr: NonNull<u8>) {
69 let owner_cpu = u16::try_from(self.cpu_id()).expect("CPU id exceeds slab owner range");
70 unsafe { SlabPageHeader::remote_free_object(ptr, owner_cpu, self.page_size()) };
71 }
72}
73
74pub trait SlabPoolTrait: Sync {
76 fn current_slab(&self) -> &dyn SlabTrait;
78
79 fn owner_slab(&self, cpu_idx: usize) -> &dyn SlabTrait;
81
82 fn current_cpu_id(&self) -> usize {
84 self.current_slab().cpu_id()
85 }
86
87 fn alloc(&self, layout: Layout) -> AllocResult<SlabAllocResult> {
89 self.current_slab().alloc(layout)
90 }
91
92 fn add_slab(&self, size_class: SizeClass, base: usize, bytes: usize) {
94 self.current_slab().add_slab(size_class, base, bytes)
95 }
96
97 fn dealloc(&self, ptr: NonNull<u8>, layout: Layout, owner_cpu: usize) -> SlabPoolDeallocResult {
99 if owner_cpu == self.current_cpu_id() {
100 match self.current_slab().dealloc_local(ptr, layout) {
101 SlabDeallocResult::Done => SlabPoolDeallocResult::Done,
102 SlabDeallocResult::FreeSlab { base, pages } => {
103 SlabPoolDeallocResult::FreeSlab { base, pages }
104 }
105 }
106 } else {
107 self.owner_slab(owner_cpu).dealloc_remote(ptr);
108 SlabPoolDeallocResult::RemoteQueued
109 }
110 }
111}
112
113pub trait SlabPoolExt: SlabPoolTrait {
115 fn with_current_slab<R>(&self, f: impl FnOnce(&dyn SlabTrait) -> R) -> R {
117 f(self.current_slab())
118 }
119
120 fn with_owner_slab<R>(&self, cpu_idx: usize, f: impl FnOnce(&dyn SlabTrait) -> R) -> R {
122 f(self.owner_slab(cpu_idx))
123 }
124}
125
126impl<T: ?Sized + SlabPoolTrait> SlabPoolExt for T {}
127
128pub struct SlabAllocator<const PAGE_SIZE: usize = 0x1000> {
130 caches: [SlabCache; SizeClass::COUNT],
131}
132
133pub struct PerCpuSlab<const PAGE_SIZE: usize = 0x1000> {
135 cpu_id: u16,
136 inner: SpinLock<SlabAllocator<PAGE_SIZE>>,
137}
138
139pub struct StaticSlabPool<const PAGE_SIZE: usize = 0x1000, const N: usize = 1> {
141 slabs: [PerCpuSlab<PAGE_SIZE>; N],
142 current_cpu_id: fn() -> usize,
143}
144
145impl<const PAGE_SIZE: usize> PerCpuSlab<PAGE_SIZE> {
146 pub const fn new(cpu_id: u16) -> Self {
148 Self {
149 cpu_id,
150 inner: SpinLock::new(SlabAllocator::new()),
151 }
152 }
153
154 #[inline]
155 fn inner(&self) -> RawSpinLockGuard<'_, SlabAllocator<PAGE_SIZE>> {
156 unsafe { self.inner.lock_raw() }
159 }
160
161 pub fn reset(&self) {
163 *self.inner() = SlabAllocator::new();
164 }
165
166 pub const fn cpu_id(&self) -> usize {
168 self.cpu_id as usize
169 }
170
171 pub fn alloc(&self, layout: Layout) -> AllocResult<SlabAllocResult> {
173 self.inner().alloc(layout)
174 }
175
176 pub fn add_slab(&self, size_class: SizeClass, base: usize, bytes: usize) {
178 self.inner().add_slab(size_class, base, bytes, self.cpu_id);
179 }
180
181 pub fn dealloc_local(&self, ptr: NonNull<u8>, layout: Layout) -> SlabDeallocResult {
183 self.inner().dealloc(ptr, layout)
184 }
185
186 pub fn dealloc_remote(&self, ptr: NonNull<u8>) {
188 unsafe { SlabPageHeader::remote_free_object(ptr, self.cpu_id, PAGE_SIZE) };
189 }
190}
191
192impl<const PAGE_SIZE: usize, const N: usize> StaticSlabPool<PAGE_SIZE, N> {
193 pub const fn new(slabs: [PerCpuSlab<PAGE_SIZE>; N], current_cpu_id: fn() -> usize) -> Self {
195 Self {
196 slabs,
197 current_cpu_id,
198 }
199 }
200}
201
202impl<const PAGE_SIZE: usize> SlabAllocator<PAGE_SIZE> {
203 pub const fn new() -> Self {
205 Self {
206 caches: [
207 SlabCache::new(SizeClass::Bytes8),
208 SlabCache::new(SizeClass::Bytes16),
209 SlabCache::new(SizeClass::Bytes32),
210 SlabCache::new(SizeClass::Bytes64),
211 SlabCache::new(SizeClass::Bytes128),
212 SlabCache::new(SizeClass::Bytes256),
213 SlabCache::new(SizeClass::Bytes512),
214 SlabCache::new(SizeClass::Bytes1024),
215 SlabCache::new(SizeClass::Bytes2048),
216 ],
217 }
218 }
219}
220
221impl<const PAGE_SIZE: usize> Default for SlabAllocator<PAGE_SIZE> {
222 fn default() -> Self {
223 Self::new()
224 }
225}
226
227impl<const PAGE_SIZE: usize> SlabAllocator<PAGE_SIZE> {
228 pub fn alloc(&mut self, layout: Layout) -> AllocResult<SlabAllocResult> {
233 let sc = SizeClass::from_layout(layout).ok_or(AllocError::InvalidParam)?;
234 let cache = &mut self.caches[sc.index()];
235
236 match cache.alloc_object::<PAGE_SIZE>() {
237 Some(addr) => {
238 let ptr = unsafe { NonNull::new_unchecked(addr as *mut u8) };
240 Ok(SlabAllocResult::Allocated(ptr))
241 }
242 None => Ok(SlabAllocResult::NeedsSlab {
243 size_class: sc,
244 pages: sc.slab_pages(PAGE_SIZE),
245 }),
246 }
247 }
248
249 pub fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) -> SlabDeallocResult {
254 let sc = SizeClass::from_layout(layout).expect("layout exceeds slab size");
255 let cache = &mut self.caches[sc.index()];
256
257 match cache.dealloc_object::<PAGE_SIZE>(ptr.as_ptr() as usize) {
258 CacheDeallocResult::Done => SlabDeallocResult::Done,
259 CacheDeallocResult::FreeSlab { base, pages } => {
260 SlabDeallocResult::FreeSlab { base, pages }
261 }
262 }
263 }
264
265 pub fn add_slab(&mut self, size_class: SizeClass, base: usize, bytes: usize, owner_cpu: u16) {
269 self.caches[size_class.index()].add_slab(base, bytes, owner_cpu);
270 }
271}
272
273impl<const PAGE_SIZE: usize> SlabTrait for PerCpuSlab<PAGE_SIZE> {
274 fn cpu_id(&self) -> usize {
275 PerCpuSlab::cpu_id(self)
276 }
277
278 fn page_size(&self) -> usize {
279 PAGE_SIZE
280 }
281
282 fn alloc(&self, layout: Layout) -> AllocResult<SlabAllocResult> {
283 PerCpuSlab::alloc(self, layout)
284 }
285
286 fn add_slab(&self, size_class: SizeClass, base: usize, bytes: usize) {
287 PerCpuSlab::add_slab(self, size_class, base, bytes)
288 }
289
290 fn dealloc_local(&self, ptr: NonNull<u8>, layout: Layout) -> SlabDeallocResult {
291 PerCpuSlab::dealloc_local(self, ptr, layout)
292 }
293}
294
295impl<const PAGE_SIZE: usize, const N: usize> SlabPoolTrait for StaticSlabPool<PAGE_SIZE, N> {
296 fn current_slab(&self) -> &dyn SlabTrait {
297 &self.slabs[(self.current_cpu_id)()]
298 }
299
300 fn owner_slab(&self, cpu_idx: usize) -> &dyn SlabTrait {
301 &self.slabs[cpu_idx]
302 }
303}