mod balancing;
use core::{
alloc::Layout,
cell::RefCell,
ops::DerefMut,
sync::atomic::{AtomicUsize, Ordering},
};
use ostd::{
cpu_local,
irq::DisabledLocalIrqGuard,
mm::Paddr,
sync::{LocalIrqDisabled, SpinLock, SpinLockGuard},
};
use crate::chunk::{BuddyOrder, greater_order_of, lesser_order_of, size_of_order, split_to_chunks};
use super::set::BuddySet;
static GLOBAL_POOL: SpinLock<BuddySet<MAX_BUDDY_ORDER>, LocalIrqDisabled> =
SpinLock::new(BuddySet::new_empty());
static GLOBAL_POOL_SIZE: AtomicUsize = AtomicUsize::new(0);
cpu_local! {
static LOCAL_POOL: RefCell<BuddySet<MAX_LOCAL_BUDDY_ORDER>> = RefCell::new(BuddySet::new_empty());
}
const MAX_BUDDY_ORDER: BuddyOrder = 32;
const MAX_LOCAL_BUDDY_ORDER: BuddyOrder = 18;
pub(super) fn alloc(guard: &DisabledLocalIrqGuard, layout: Layout) -> Option<Paddr> {
let local_pool_cell = LOCAL_POOL.get_with(guard);
let mut local_pool = local_pool_cell.borrow_mut();
let mut global_pool = OnDemandGlobalLock::new();
let size_order = greater_order_of(layout.size());
let align_order = greater_order_of(layout.align());
let order = size_order.max(align_order);
let mut chunk_addr = None;
if order < MAX_LOCAL_BUDDY_ORDER {
chunk_addr = local_pool.alloc_chunk(order);
}
if chunk_addr.is_none() {
chunk_addr = global_pool.get().alloc_chunk(order);
}
let allocated_size = size_of_order(order);
if allocated_size > layout.size()
&& let Some(chunk_addr) = chunk_addr
{
do_dealloc(
&mut local_pool,
&mut global_pool,
[(chunk_addr + layout.size(), allocated_size - layout.size())].into_iter(),
);
}
balancing::balance(local_pool.deref_mut(), &mut global_pool);
chunk_addr
}
pub(super) fn dealloc(
guard: &DisabledLocalIrqGuard,
segments: impl Iterator<Item = (Paddr, usize)>,
) {
let local_pool_cell = LOCAL_POOL.get_with(guard);
let mut local_pool = local_pool_cell.borrow_mut();
let mut global_pool = OnDemandGlobalLock::new();
do_dealloc(&mut local_pool, &mut global_pool, segments);
balancing::balance(local_pool.deref_mut(), &mut global_pool);
}
pub(super) fn add_free_memory(_guard: &DisabledLocalIrqGuard, addr: Paddr, size: usize) {
let mut global_pool = OnDemandGlobalLock::new();
split_to_chunks(addr, size).for_each(|(addr, order)| {
global_pool.get().insert_chunk(addr, order);
});
}
fn do_dealloc(
local_pool: &mut BuddySet<MAX_LOCAL_BUDDY_ORDER>,
global_pool: &mut OnDemandGlobalLock,
segments: impl Iterator<Item = (Paddr, usize)>,
) {
segments.for_each(|(addr, size)| {
split_to_chunks(addr, size).for_each(|(addr, order)| {
if order >= MAX_LOCAL_BUDDY_ORDER {
global_pool.get().insert_chunk(addr, order);
} else {
local_pool.insert_chunk(addr, order);
}
});
});
}
type GlobalLockGuard = SpinLockGuard<'static, BuddySet<MAX_BUDDY_ORDER>, LocalIrqDisabled>;
struct OnDemandGlobalLock {
guard: Option<GlobalLockGuard>,
}
impl OnDemandGlobalLock {
fn new() -> Self {
Self { guard: None }
}
fn get(&mut self) -> &mut GlobalLockGuard {
self.guard.get_or_insert_with(|| GLOBAL_POOL.lock())
}
fn get_global_size(&self) -> usize {
if let Some(guard) = self.guard.as_ref() {
guard.total_size()
} else {
GLOBAL_POOL_SIZE.load(Ordering::Relaxed)
}
}
}
impl Drop for OnDemandGlobalLock {
fn drop(&mut self) {
if let Some(guard) = self.guard.as_ref() {
GLOBAL_POOL_SIZE.store(guard.total_size(), Ordering::Relaxed);
}
}
}