1use core::{
4 alloc::{GlobalAlloc, Layout},
5 ptr::NonNull,
6 slice,
7};
8
9use ax_kspin::SpinNoIrq;
10use buddy_slab_allocator::{
11 GlobalAllocator as InnerAllocator, SizeClass, SlabAllocResult, SlabAllocator,
12 SlabDeallocResult, SlabPoolTrait, SlabTrait,
13 eii::{slab_pool_impl, virt_to_phys_impl},
14};
15
16use super::{AllocResult, AllocatorOps, UsageKind, Usages};
17
18#[cfg_attr(all(target_os = "none", not(test)), global_allocator)]
20static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator::new();
21
22pub type DefaultByteAllocator = buddy_slab_allocator::SlabAllocator<PAGE_SIZE>;
24
25const PAGE_SIZE: usize = 0x1000;
26
27#[ax_percpu::def_percpu]
28static PERCPU_SLAB: PercpuSlab<PAGE_SIZE> = PercpuSlab::new_uninit();
29
30static SLAB_POOL: SlabPool = SlabPool;
31
32struct PercpuSlab<const PAGE_SIZE: usize = 0x1000> {
33 cpu_id: Option<u16>,
34 inner: SpinNoIrq<SlabAllocator<PAGE_SIZE>>,
35}
36
37impl<const PAGE_SIZE: usize> PercpuSlab<PAGE_SIZE> {
38 const fn new_uninit() -> Self {
39 Self {
40 cpu_id: None,
41 inner: SpinNoIrq::new(SlabAllocator::new()),
42 }
43 }
44
45 fn init(&mut self, cpu_id: usize) {
46 let cpu_id = u16::try_from(cpu_id).expect("CPU id exceeds per-CPU slab range");
47 assert!(
48 self.cpu_id.is_none(),
49 "per-CPU slab is already initialized on this CPU",
50 );
51 self.cpu_id = Some(cpu_id);
52 *self.inner.lock() = SlabAllocator::new();
53 }
54
55 fn cpu_id_checked(&self) -> u16 {
56 self.cpu_id
57 .expect("per-CPU slab is not initialized on this CPU")
58 }
59}
60
61impl<const PAGE_SIZE: usize> SlabTrait for PercpuSlab<PAGE_SIZE> {
62 fn cpu_id(&self) -> usize {
63 self.cpu_id_checked() as usize
64 }
65
66 fn page_size(&self) -> usize {
67 PAGE_SIZE
68 }
69
70 fn alloc(&self, layout: Layout) -> buddy_slab_allocator::AllocResult<SlabAllocResult> {
71 self.inner.lock().alloc(layout)
72 }
73
74 fn add_slab(&self, size_class: SizeClass, base: usize, bytes: usize) {
75 self.inner
76 .lock()
77 .add_slab(size_class, base, bytes, self.cpu_id_checked());
78 }
79
80 fn dealloc_local(&self, ptr: NonNull<u8>, layout: Layout) -> SlabDeallocResult {
81 self.inner.lock().dealloc(ptr, layout)
82 }
83}
84
85fn current_percpu_slab() -> &'static PercpuSlab<PAGE_SIZE> {
86 unsafe { PERCPU_SLAB.current_ref_raw() }
89}
90
91fn remote_percpu_slab(cpu_idx: usize) -> &'static PercpuSlab<PAGE_SIZE> {
92 unsafe { PERCPU_SLAB.remote_ref_raw(cpu_idx) }
95}
96
97struct SlabPool;
98
99impl SlabPoolTrait for SlabPool {
100 fn current_slab(&self) -> &dyn SlabTrait {
101 current_percpu_slab()
102 }
103
104 fn owner_slab(&self, cpu_idx: usize) -> &dyn SlabTrait {
105 remote_percpu_slab(cpu_idx)
106 }
107}
108
109#[slab_pool_impl]
110fn slab_pool() -> &'static dyn SlabPoolTrait {
111 &SLAB_POOL
112}
113
114#[virt_to_phys_impl]
115fn virt_to_phys(vaddr: usize) -> usize {
116 ax_plat::mem::virt_to_phys(vaddr.into()).as_usize()
117}
118
119pub struct GlobalAllocator {
121 inner: SpinNoIrq<InnerAllocator<PAGE_SIZE>>,
122 usages: SpinNoIrq<Usages>,
123}
124
125impl Default for GlobalAllocator {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131impl GlobalAllocator {
132 pub const fn new() -> Self {
134 Self {
135 inner: SpinNoIrq::new(InnerAllocator::<PAGE_SIZE>::new()),
136 usages: SpinNoIrq::new(Usages::new()),
137 }
138 }
139
140 pub const fn name(&self) -> &'static str {
142 "buddy-slab-allocator"
143 }
144
145 pub fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
147 info!(
148 "Initialize global memory allocator, start_vaddr: {:#x}, size: {:#x}",
149 start_vaddr, size
150 );
151 let region = unsafe { slice::from_raw_parts_mut(start_vaddr as *mut u8, size) };
152 unsafe { self.inner.lock().init(region) }.map_err(Into::into)
153 }
154
155 pub fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
157 info!(
158 "Add memory region, start_vaddr: {:#x}, size: {:#x}",
159 start_vaddr, size
160 );
161 let region = unsafe { slice::from_raw_parts_mut(start_vaddr as *mut u8, size) };
162 unsafe { self.inner.lock().add_region(region) }.map_err(Into::into)
163 }
164
165 pub fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
168 let result = self
169 .inner
170 .lock()
171 .alloc(layout)
172 .map_err(crate::AllocError::from);
173 if result.is_ok() {
174 self.usages.lock().alloc(UsageKind::RustHeap, layout.size());
175 }
176 result
177 }
178
179 pub fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
181 self.usages
182 .lock()
183 .dealloc(UsageKind::RustHeap, layout.size());
184 unsafe { self.inner.lock().dealloc(pos, layout) };
185 }
186
187 pub fn alloc_pages(
189 &self,
190 num_pages: usize,
191 alignment: usize,
192 kind: UsageKind,
193 ) -> AllocResult<usize> {
194 let result = self
195 .inner
196 .lock()
197 .alloc_pages(num_pages, alignment)
198 .map_err(crate::AllocError::from);
199 if result.is_ok() {
200 self.usages.lock().alloc(kind, num_pages * PAGE_SIZE);
201 }
202 result
203 }
204
205 pub fn alloc_dma32_pages(
207 &self,
208 num_pages: usize,
209 alignment: usize,
210 kind: UsageKind,
211 ) -> AllocResult<usize> {
212 let result = self
213 .inner
214 .lock()
215 .alloc_pages_lowmem(num_pages, alignment)
216 .map_err(crate::AllocError::from);
217 if result.is_ok() {
218 self.usages.lock().alloc(kind, num_pages * PAGE_SIZE);
219 }
220 result
221 }
222
223 pub fn alloc_pages_at(
225 &self,
226 _start: usize,
227 _num_pages: usize,
228 _alignment: usize,
229 _kind: UsageKind,
230 ) -> AllocResult<usize> {
231 unimplemented!("buddy-slab allocator does not support alloc_pages_at")
232 }
233
234 pub fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
236 self.usages.lock().dealloc(kind, num_pages * PAGE_SIZE);
237 self.inner.lock().dealloc_pages(pos, num_pages);
238 }
239
240 pub fn used_bytes(&self) -> usize {
242 self.inner.lock().allocated_bytes()
243 }
244
245 pub fn available_bytes(&self) -> usize {
247 let inner = self.inner.lock();
248 inner
249 .managed_bytes()
250 .saturating_sub(inner.allocated_bytes())
251 }
252
253 pub fn used_pages(&self) -> usize {
255 self.used_bytes() / PAGE_SIZE
256 }
257
258 pub fn available_pages(&self) -> usize {
260 self.available_bytes() / PAGE_SIZE
261 }
262
263 pub fn usages(&self) -> Usages {
265 *self.usages.lock()
266 }
267}
268
269impl AllocatorOps for GlobalAllocator {
270 fn name(&self) -> &'static str {
271 GlobalAllocator::name(self)
272 }
273
274 fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
275 GlobalAllocator::init(self, start_vaddr, size)
276 }
277
278 fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
279 GlobalAllocator::add_memory(self, start_vaddr, size)
280 }
281
282 fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
283 GlobalAllocator::alloc(self, layout)
284 }
285
286 fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
287 GlobalAllocator::dealloc(self, pos, layout)
288 }
289
290 fn alloc_pages(
291 &self,
292 num_pages: usize,
293 alignment: usize,
294 kind: UsageKind,
295 ) -> AllocResult<usize> {
296 GlobalAllocator::alloc_pages(self, num_pages, alignment, kind)
297 }
298
299 fn alloc_dma32_pages(
300 &self,
301 num_pages: usize,
302 alignment: usize,
303 kind: UsageKind,
304 ) -> AllocResult<usize> {
305 GlobalAllocator::alloc_dma32_pages(self, num_pages, alignment, kind)
306 }
307
308 fn alloc_pages_at(
309 &self,
310 start: usize,
311 num_pages: usize,
312 alignment: usize,
313 kind: UsageKind,
314 ) -> AllocResult<usize> {
315 GlobalAllocator::alloc_pages_at(self, start, num_pages, alignment, kind)
316 }
317
318 fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
319 GlobalAllocator::dealloc_pages(self, pos, num_pages, kind)
320 }
321
322 fn used_bytes(&self) -> usize {
323 GlobalAllocator::used_bytes(self)
324 }
325
326 fn available_bytes(&self) -> usize {
327 GlobalAllocator::available_bytes(self)
328 }
329
330 fn used_pages(&self) -> usize {
331 GlobalAllocator::used_pages(self)
332 }
333
334 fn available_pages(&self) -> usize {
335 GlobalAllocator::available_pages(self)
336 }
337
338 fn usages(&self) -> Usages {
339 GlobalAllocator::usages(self)
340 }
341}
342
343pub fn global_allocator() -> &'static GlobalAllocator {
345 &GLOBAL_ALLOCATOR
346}
347
348pub fn init_percpu_slab(cpu_id: usize) {
350 PERCPU_SLAB.with_current(|slab| slab.init(cpu_id));
351}
352
353pub fn global_init(start_vaddr: usize, size: usize) -> AllocResult {
355 debug!(
356 "initialize global allocator at: [{:#x}, {:#x})",
357 start_vaddr,
358 start_vaddr + size
359 );
360 GLOBAL_ALLOCATOR.init(start_vaddr, size)?;
361 info!("global allocator initialized");
362 Ok(())
363}
364
365pub fn global_add_memory(start_vaddr: usize, size: usize) -> AllocResult {
367 debug!(
368 "add a memory region to global allocator: [{:#x}, {:#x})",
369 start_vaddr,
370 start_vaddr + size
371 );
372 GLOBAL_ALLOCATOR.add_memory(start_vaddr, size)
373}
374
375unsafe impl GlobalAlloc for GlobalAllocator {
376 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
377 let inner = move || {
378 if let Ok(ptr) = GlobalAllocator::alloc(self, layout) {
379 ptr.as_ptr()
380 } else {
381 alloc::alloc::handle_alloc_error(layout)
382 }
383 };
384
385 #[cfg(feature = "tracking")]
386 {
387 crate::tracking::with_state(|state| match state {
388 None => inner(),
389 Some(state) => {
390 let ptr = inner();
391 let generation = state.generation;
392 state.generation += 1;
393 state.map.insert(
394 ptr as usize,
395 crate::tracking::AllocationInfo {
396 layout,
397 backtrace: axbacktrace::Backtrace::capture(),
398 generation,
399 },
400 );
401 ptr
402 }
403 })
404 }
405
406 #[cfg(not(feature = "tracking"))]
407 inner()
408 }
409
410 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
411 let ptr = NonNull::new(ptr).expect("dealloc null ptr");
412 let inner = || GlobalAllocator::dealloc(self, ptr, layout);
413
414 #[cfg(feature = "tracking")]
415 crate::tracking::with_state(|state| match state {
416 None => inner(),
417 Some(state) => {
418 let address = ptr.as_ptr() as usize;
419 state.map.remove(&address);
420 inner()
421 }
422 });
423
424 #[cfg(not(feature = "tracking"))]
425 inner();
426 }
427}
428
429impl From<buddy_slab_allocator::AllocError> for super::AllocError {
430 fn from(value: buddy_slab_allocator::AllocError) -> Self {
431 match value {
432 buddy_slab_allocator::AllocError::InvalidParam => Self::InvalidParam,
433 buddy_slab_allocator::AllocError::AlreadyInitialized => Self::AlreadyInitialized,
434 buddy_slab_allocator::AllocError::MemoryOverlap => Self::MemoryOverlap,
435 buddy_slab_allocator::AllocError::NoMemory => Self::NoMemory,
436 buddy_slab_allocator::AllocError::NotAllocated => Self::NotAllocated,
437 buddy_slab_allocator::AllocError::NotInitialized => Self::NotInitialized,
438 buddy_slab_allocator::AllocError::NotFound => Self::NotFound,
439 }
440 }
441}