#![allow(dead_code)]
use core::{
alloc::{GlobalAlloc, Layout},
ptr::NonNull,
};
use ax_allocator::{BaseAllocator, BitmapPageAllocator, ByteAllocator, PageAllocator};
use ax_kspin::SpinNoIrq;
use super::{AllocResult, AllocatorOps, UsageKind, Usages};
#[cfg_attr(all(target_os = "none", not(test)), global_allocator)]
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator::new();
const PAGE_SIZE: usize = 0x1000;
const MIN_HEAP_SIZE: usize = 0x8000;
cfg_if::cfg_if! {
if #[cfg(feature = "slab")] {
pub type DefaultByteAllocator = ax_allocator::SlabByteAllocator;
} else if #[cfg(feature = "buddy")] {
pub type DefaultByteAllocator = ax_allocator::BuddyByteAllocator;
} else if #[cfg(feature = "tlsf")] {
pub type DefaultByteAllocator = ax_allocator::TlsfByteAllocator;
}
}
pub struct GlobalAllocator {
balloc: SpinNoIrq<DefaultByteAllocator>,
palloc: SpinNoIrq<BitmapPageAllocator<PAGE_SIZE>>,
usages: SpinNoIrq<Usages>,
}
impl Default for GlobalAllocator {
fn default() -> Self {
Self::new()
}
}
impl GlobalAllocator {
pub const fn new() -> Self {
Self {
balloc: SpinNoIrq::new(DefaultByteAllocator::new()),
palloc: SpinNoIrq::new(BitmapPageAllocator::new()),
usages: SpinNoIrq::new(Usages::new()),
}
}
pub const fn name(&self) -> &'static str {
cfg_if::cfg_if! {
if #[cfg(feature = "slab")] {
"slab"
} else if #[cfg(feature = "buddy")] {
"buddy"
} else if #[cfg(feature = "tlsf")] {
"TLSF"
} else {
"unknown"
}
}
}
pub fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
if size <= MIN_HEAP_SIZE {
return Err(crate::AllocError::InvalidParam);
}
let init_heap_size = MIN_HEAP_SIZE;
self.palloc.lock().init(start_vaddr, size);
let heap_ptr =
self.alloc_pages(init_heap_size / PAGE_SIZE, PAGE_SIZE, UsageKind::RustHeap)?;
self.balloc.lock().init(heap_ptr, init_heap_size);
Ok(())
}
pub fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
self.balloc
.lock()
.add_memory(start_vaddr, size)
.map_err(Into::into)
}
pub fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
let mut balloc = self.balloc.lock();
loop {
if let Ok(ptr) = balloc.alloc(layout) {
self.usages.lock().alloc(UsageKind::RustHeap, layout.size());
return Ok(ptr);
} else {
let old_size = balloc.total_bytes();
let expand_size = old_size
.max(layout.size())
.next_power_of_two()
.max(PAGE_SIZE);
let mut try_size = expand_size;
let min_size = PAGE_SIZE.max(layout.size());
loop {
let heap_ptr = match self.alloc_pages(
try_size / PAGE_SIZE,
PAGE_SIZE,
UsageKind::RustHeap,
) {
Ok(ptr) => ptr,
Err(err) => {
try_size /= 2;
if try_size < min_size {
return Err(err);
}
continue;
}
};
debug!(
"expand heap memory: [{:#x}, {:#x})",
heap_ptr,
heap_ptr + try_size
);
balloc
.add_memory(heap_ptr, try_size)
.map_err(crate::AllocError::from)?;
break;
}
}
}
}
pub fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
self.usages
.lock()
.dealloc(UsageKind::RustHeap, layout.size());
self.balloc.lock().dealloc(pos, layout)
}
pub fn alloc_pages(
&self,
num_pages: usize,
alignment: usize,
kind: UsageKind,
) -> AllocResult<usize> {
let addr = self
.palloc
.lock()
.alloc_pages(num_pages, alignment)
.map_err(crate::AllocError::from)?;
if !matches!(kind, UsageKind::RustHeap) {
self.usages.lock().alloc(kind, num_pages * PAGE_SIZE);
}
Ok(addr)
}
pub fn alloc_dma32_pages(
&self,
_num_pages: usize,
_alignment: usize,
_kind: UsageKind,
) -> AllocResult<usize> {
unimplemented!("default allocator does not support alloc_dma32_pages")
}
pub fn alloc_pages_at(
&self,
start: usize,
num_pages: usize,
alignment: usize,
kind: UsageKind,
) -> AllocResult<usize> {
let addr = self
.palloc
.lock()
.alloc_pages_at(start, num_pages, alignment)
.map_err(crate::AllocError::from)?;
if !matches!(kind, UsageKind::RustHeap) {
self.usages.lock().alloc(kind, num_pages * PAGE_SIZE);
}
Ok(addr)
}
pub fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
self.usages.lock().dealloc(kind, num_pages * PAGE_SIZE);
self.palloc.lock().dealloc_pages(pos, num_pages);
}
pub fn used_bytes(&self) -> usize {
self.balloc.lock().used_bytes()
}
pub fn available_bytes(&self) -> usize {
self.balloc.lock().available_bytes()
}
pub fn used_pages(&self) -> usize {
self.palloc.lock().used_pages()
}
pub fn available_pages(&self) -> usize {
self.palloc.lock().available_pages()
}
pub fn usages(&self) -> Usages {
*self.usages.lock()
}
}
impl AllocatorOps for GlobalAllocator {
fn name(&self) -> &'static str {
GlobalAllocator::name(self)
}
fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
GlobalAllocator::init(self, start_vaddr, size)
}
fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
GlobalAllocator::add_memory(self, start_vaddr, size)
}
fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
GlobalAllocator::alloc(self, layout)
}
fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
GlobalAllocator::dealloc(self, pos, layout)
}
fn alloc_pages(
&self,
num_pages: usize,
alignment: usize,
kind: UsageKind,
) -> AllocResult<usize> {
GlobalAllocator::alloc_pages(self, num_pages, alignment, kind)
}
fn alloc_dma32_pages(
&self,
num_pages: usize,
alignment: usize,
kind: UsageKind,
) -> AllocResult<usize> {
GlobalAllocator::alloc_dma32_pages(self, num_pages, alignment, kind)
}
fn alloc_pages_at(
&self,
start: usize,
num_pages: usize,
alignment: usize,
kind: UsageKind,
) -> AllocResult<usize> {
GlobalAllocator::alloc_pages_at(self, start, num_pages, alignment, kind)
}
fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
GlobalAllocator::dealloc_pages(self, pos, num_pages, kind)
}
fn used_bytes(&self) -> usize {
GlobalAllocator::used_bytes(self)
}
fn available_bytes(&self) -> usize {
GlobalAllocator::available_bytes(self)
}
fn used_pages(&self) -> usize {
GlobalAllocator::used_pages(self)
}
fn available_pages(&self) -> usize {
GlobalAllocator::available_pages(self)
}
fn usages(&self) -> Usages {
GlobalAllocator::usages(self)
}
}
pub fn global_allocator() -> &'static GlobalAllocator {
&GLOBAL_ALLOCATOR
}
pub fn global_init(start_vaddr: usize, size: usize) -> AllocResult {
debug!(
"initialize global allocator at: [{:#x}, {:#x})",
start_vaddr,
start_vaddr + size
);
GLOBAL_ALLOCATOR.init(start_vaddr, size)
}
pub fn global_add_memory(start_vaddr: usize, size: usize) -> AllocResult {
debug!(
"add a memory region to global allocator: [{:#x}, {:#x})",
start_vaddr,
start_vaddr + size
);
GLOBAL_ALLOCATOR.add_memory(start_vaddr, size)
}
unsafe impl GlobalAlloc for GlobalAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let inner = move || {
if let Ok(ptr) = GlobalAllocator::alloc(self, layout) {
ptr.as_ptr()
} else {
alloc::alloc::handle_alloc_error(layout)
}
};
#[cfg(feature = "tracking")]
{
crate::tracking::with_state(|state| match state {
None => inner(),
Some(state) => {
let ptr = inner();
let generation = state.generation;
state.generation += 1;
state.map.insert(
ptr as usize,
crate::tracking::AllocationInfo {
layout,
backtrace: axbacktrace::Backtrace::capture(),
generation,
},
);
ptr
}
})
}
#[cfg(not(feature = "tracking"))]
inner()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let ptr = NonNull::new(ptr).expect("dealloc null ptr");
let inner = || GlobalAllocator::dealloc(self, ptr, layout);
#[cfg(feature = "tracking")]
crate::tracking::with_state(|state| match state {
None => inner(),
Some(state) => {
let address = ptr.as_ptr() as usize;
state.map.remove(&address);
inner()
}
});
#[cfg(not(feature = "tracking"))]
inner();
}
}
impl From<ax_allocator::AllocError> for super::AllocError {
fn from(value: ax_allocator::AllocError) -> Self {
match value {
ax_allocator::AllocError::InvalidParam => Self::InvalidParam,
ax_allocator::AllocError::MemoryOverlap => Self::MemoryOverlap,
ax_allocator::AllocError::NoMemory => Self::NoMemory,
ax_allocator::AllocError::NotAllocated => Self::NotAllocated,
}
}
}