mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 07:06:47 +00:00
feat: 释放slab中的空闲页面到buddy (#932)
* patch: 释放slab中的空闲页面到buddy * 校验释放的slab_page的起始地址与大小 & SCAllcator增加空闲块计数器
This commit is contained in:
parent
79eda4bcf9
commit
53629ac383
@ -65,7 +65,12 @@ pub enum AllocationError {
|
|||||||
/// Needs to adhere to safety requirements of a rust allocator (see GlobalAlloc et. al.).
|
/// Needs to adhere to safety requirements of a rust allocator (see GlobalAlloc et. al.).
|
||||||
pub unsafe trait Allocator<'a> {
|
pub unsafe trait Allocator<'a> {
|
||||||
fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError>;
|
fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError>;
|
||||||
fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError>;
|
unsafe fn deallocate(
|
||||||
|
&mut self,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
layout: Layout,
|
||||||
|
slab_callback: &'static dyn CallBack,
|
||||||
|
) -> Result<(), AllocationError>;
|
||||||
|
|
||||||
/// Refill the allocator with a [`ObjectPage`].
|
/// Refill the allocator with a [`ObjectPage`].
|
||||||
///
|
///
|
||||||
@ -77,3 +82,8 @@ pub unsafe trait Allocator<'a> {
|
|||||||
new_page: &'a mut ObjectPage<'a>,
|
new_page: &'a mut ObjectPage<'a>,
|
||||||
) -> Result<(), AllocationError>;
|
) -> Result<(), AllocationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 将slab_page归还Buddy的回调函数
|
||||||
|
pub trait CallBack: Send + Sync {
|
||||||
|
unsafe fn free_slab_page(&self, _: *mut u8, _: usize) {}
|
||||||
|
}
|
||||||
|
@ -255,6 +255,20 @@ pub trait AllocablePage {
|
|||||||
self.bitfield().clear_bit(idx);
|
self.bitfield().clear_bit(idx);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 统计page中还可以分配多少个object
|
||||||
|
fn free_obj_count(&self) -> usize {
|
||||||
|
// 统计page中还可以分配多少个object
|
||||||
|
let mut free_obj_count = 0;
|
||||||
|
|
||||||
|
// 遍历page中的bitfield(用来统计内存分配情况的u64数组)
|
||||||
|
for b in self.bitfield().iter() {
|
||||||
|
let bitval = b.load(Ordering::Relaxed);
|
||||||
|
free_obj_count += bitval.count_zeros() as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_obj_count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds allocated data within a 4 KiB page.
|
/// Holds allocated data within a 4 KiB page.
|
||||||
|
@ -59,21 +59,29 @@ pub struct SCAllocator<'a, P: AllocablePage> {
|
|||||||
pub(crate) slabs: PageList<'a, P>,
|
pub(crate) slabs: PageList<'a, P>,
|
||||||
/// List of full ObjectPages (everything allocated in these don't need to search them).
|
/// List of full ObjectPages (everything allocated in these don't need to search them).
|
||||||
pub(crate) full_slabs: PageList<'a, P>,
|
pub(crate) full_slabs: PageList<'a, P>,
|
||||||
|
/// Free objects count
|
||||||
|
pub(crate) free_obj_count: usize,
|
||||||
|
/// Maximum free objects num for this `SCAllocator`.
|
||||||
|
pub(crate) free_limit: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an instance of a scallocator, we do this in a macro because we
|
/// Creates an instance of a scallocator, we do this in a macro because we
|
||||||
/// re-use the code in const and non-const functions
|
/// re-use the code in const and non-const functions
|
||||||
macro_rules! new_sc_allocator {
|
macro_rules! new_sc_allocator {
|
||||||
($size:expr) => {
|
($size:expr) => {{
|
||||||
|
let obj_per_page = cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64);
|
||||||
SCAllocator {
|
SCAllocator {
|
||||||
size: $size,
|
size: $size,
|
||||||
allocation_count: 0,
|
allocation_count: 0,
|
||||||
obj_per_page: cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64),
|
obj_per_page,
|
||||||
empty_slabs: PageList::new(),
|
empty_slabs: PageList::new(),
|
||||||
slabs: PageList::new(),
|
slabs: PageList::new(),
|
||||||
full_slabs: PageList::new(),
|
full_slabs: PageList::new(),
|
||||||
|
// TODO: 优化free_limit的计算: https://bbs.dragonos.org.cn/t/topic/358
|
||||||
|
free_limit: 2 * obj_per_page,
|
||||||
|
free_obj_count: 0,
|
||||||
}
|
}
|
||||||
};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
||||||
@ -241,6 +249,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
|||||||
*page.next() = Rawlink::none();
|
*page.next() = Rawlink::none();
|
||||||
trace!("adding page to SCAllocator {:p}", page);
|
trace!("adding page to SCAllocator {:p}", page);
|
||||||
self.insert_empty(page);
|
self.insert_empty(page);
|
||||||
|
self.free_obj_count += self.obj_per_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a block of memory descriped by `layout`.
|
/// Allocates a block of memory descriped by `layout`.
|
||||||
@ -294,6 +303,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
|||||||
self.size,
|
self.size,
|
||||||
ptr as usize
|
ptr as usize
|
||||||
);
|
);
|
||||||
|
self.free_obj_count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@ -304,7 +314,12 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
|||||||
/// May return an error in case an invalid `layout` is provided.
|
/// May return an error in case an invalid `layout` is provided.
|
||||||
/// The function may also move internal slab pages between lists partial -> empty
|
/// The function may also move internal slab pages between lists partial -> empty
|
||||||
/// or full -> partial lists.
|
/// or full -> partial lists.
|
||||||
pub fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> {
|
pub unsafe fn deallocate(
|
||||||
|
&mut self,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
layout: Layout,
|
||||||
|
slab_callback: &'static dyn CallBack,
|
||||||
|
) -> Result<(), AllocationError> {
|
||||||
assert!(layout.size() <= self.size);
|
assert!(layout.size() <= self.size);
|
||||||
assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD));
|
assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD));
|
||||||
trace!(
|
trace!(
|
||||||
@ -324,6 +339,17 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> {
|
|||||||
|
|
||||||
let ret = slab_page.deallocate(ptr, new_layout);
|
let ret = slab_page.deallocate(ptr, new_layout);
|
||||||
debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment");
|
debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment");
|
||||||
|
self.free_obj_count += 1;
|
||||||
|
let is_empty_after_dealloc = slab_page.is_empty(self.obj_per_page);
|
||||||
|
|
||||||
|
// 如果slab_page是空白的,且空闲块数大于free_limit,将slab_page归还buddy
|
||||||
|
if self.free_obj_count >= self.free_limit && is_empty_after_dealloc {
|
||||||
|
self.slabs.remove_from_list(slab_page);
|
||||||
|
// 将slab_page归还buddy
|
||||||
|
slab_callback.free_slab_page(slab_page as *const P as *mut u8, P::SIZE);
|
||||||
|
}
|
||||||
|
self.check_page_assignments();
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
//! The ZoneAllocator achieves this by having many `SCAllocator`
|
//! The ZoneAllocator achieves this by having many `SCAllocator`
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use core::sync::atomic::Ordering;
|
|
||||||
|
|
||||||
/// Creates an instance of a zone, we do this in a macro because we
|
/// Creates an instance of a zone, we do this in a macro because we
|
||||||
/// re-use the code in const and non-const functions
|
/// re-use the code in const and non-const functions
|
||||||
@ -139,16 +138,8 @@ impl<'a> ZoneAllocator<'a> {
|
|||||||
|
|
||||||
// 遍历scallocator中的部分分配的page(partial_page)
|
// 遍历scallocator中的部分分配的page(partial_page)
|
||||||
for slab_page in scallocator.slabs.iter_mut() {
|
for slab_page in scallocator.slabs.iter_mut() {
|
||||||
// 统计page中还可以分配多少个object
|
|
||||||
let mut free_obj_count = 0;
|
|
||||||
// 遍历page中的bitfield(用来统计内存分配情况的u64数组)
|
|
||||||
for b in slab_page.bitfield().iter() {
|
|
||||||
let bitval = b.load(Ordering::Relaxed);
|
|
||||||
let free_count = bitval.count_zeros() as usize;
|
|
||||||
free_obj_count += free_count;
|
|
||||||
}
|
|
||||||
// 剩余可分配object数乘上page中规定的每个object的大小,即空闲空间
|
// 剩余可分配object数乘上page中规定的每个object的大小,即空闲空间
|
||||||
free += free_obj_count * scallocator.size();
|
free += slab_page.free_obj_count() * scallocator.size();
|
||||||
}
|
}
|
||||||
// 遍历scallocator中的empty_page,把空页空间也加上去
|
// 遍历scallocator中的empty_page,把空页空间也加上去
|
||||||
free +=
|
free +=
|
||||||
@ -178,9 +169,15 @@ unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ptr` - Address of the memory location to free.
|
/// * `ptr` - Address of the memory location to free.
|
||||||
/// * `layout` - Memory layout of the block pointed to by `ptr`.
|
/// * `layout` - Memory layout of the block pointed to by `ptr`.
|
||||||
fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> {
|
/// * `slab_callback` - The callback function to free slab_page in buddy.
|
||||||
|
unsafe fn deallocate(
|
||||||
|
&mut self,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
layout: Layout,
|
||||||
|
slab_callback: &'static dyn CallBack,
|
||||||
|
) -> Result<(), AllocationError> {
|
||||||
match ZoneAllocator::get_slab(layout.size()) {
|
match ZoneAllocator::get_slab(layout.size()) {
|
||||||
Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout),
|
Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout, slab_callback),
|
||||||
Slab::Unsupported => Err(AllocationError::InvalidLayout),
|
Slab::Unsupported => Err(AllocationError::InvalidLayout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ impl KernelAllocator {
|
|||||||
return Ok(NonNull::from(slice));
|
return Ok(NonNull::from(slice));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn free_in_buddy(&self, ptr: *mut u8, layout: Layout) {
|
pub(super) unsafe fn free_in_buddy(&self, ptr: *mut u8, layout: Layout) {
|
||||||
// 由于buddy分配的页数量是2的幂,因此释放的时候也需要按照2的幂向上取整。
|
// 由于buddy分配的页数量是2的幂,因此释放的时候也需要按照2的幂向上取整。
|
||||||
let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two();
|
let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two();
|
||||||
let page_frame_count = PageFrameCount::new(count);
|
let page_frame_count = PageFrameCount::new(count);
|
||||||
|
@ -4,12 +4,16 @@ use alloc::boxed::Box;
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use slabmalloc::*;
|
use slabmalloc::*;
|
||||||
|
|
||||||
|
use crate::{arch::MMArch, mm::MemoryManagementArch, KERNEL_ALLOCATOR};
|
||||||
|
|
||||||
// 全局slab分配器
|
// 全局slab分配器
|
||||||
pub(crate) static mut SLABALLOCATOR: Option<SlabAllocator> = None;
|
pub(crate) static mut SLABALLOCATOR: Option<SlabAllocator> = None;
|
||||||
|
|
||||||
// slab初始化状态
|
// slab初始化状态
|
||||||
pub(crate) static mut SLABINITSTATE: AtomicBool = AtomicBool::new(false);
|
pub(crate) static mut SLABINITSTATE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
static SLAB_CALLBACK: SlabCallback = SlabCallback;
|
||||||
|
|
||||||
/// slab分配器,实际为一堆小的allocator,可以在里面装4K的page
|
/// slab分配器,实际为一堆小的allocator,可以在里面装4K的page
|
||||||
/// 利用这些allocator可以为对象分配不同大小的空间
|
/// 利用这些allocator可以为对象分配不同大小的空间
|
||||||
pub(crate) struct SlabAllocator {
|
pub(crate) struct SlabAllocator {
|
||||||
@ -52,7 +56,7 @@ impl SlabAllocator {
|
|||||||
) -> Result<(), AllocationError> {
|
) -> Result<(), AllocationError> {
|
||||||
if let Some(nptr) = NonNull::new(ptr) {
|
if let Some(nptr) = NonNull::new(ptr) {
|
||||||
self.zone
|
self.zone
|
||||||
.deallocate(nptr, layout)
|
.deallocate(nptr, layout, &SLAB_CALLBACK)
|
||||||
.expect("Couldn't deallocate");
|
.expect("Couldn't deallocate");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
@ -80,3 +84,13 @@ pub unsafe fn slab_usage() -> SlabUsage {
|
|||||||
SlabUsage::new(0, 0)
|
SlabUsage::new(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 归还slab_page给buddy的回调
|
||||||
|
pub struct SlabCallback;
|
||||||
|
impl CallBack for SlabCallback {
|
||||||
|
unsafe fn free_slab_page(&self, base_addr: *mut u8, size: usize) {
|
||||||
|
assert_eq!(base_addr as usize & (MMArch::PAGE_SIZE - 1), 0); // 确认地址4k对齐
|
||||||
|
assert_eq!(size, MMArch::PAGE_SIZE); // 确认释放的slab_page大小
|
||||||
|
KERNEL_ALLOCATOR.free_in_buddy(base_addr, Layout::from_size_align_unchecked(size, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user