From 53629ac383d56d7c889ba4fe2aa0e7cf82bd4a8e Mon Sep 17 00:00:00 2001 From: Jomo Date: Sat, 12 Oct 2024 00:57:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8A=E6=94=BEslab=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E7=A9=BA=E9=97=B2=E9=A1=B5=E9=9D=A2=E5=88=B0buddy=20(?= =?UTF-8?q?#932)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * patch: 释放slab中的空闲页面到buddy * 校验释放的slab_page的起始地址与大小 & SCAllcator增加空闲块计数器 --- kernel/crates/rust-slabmalloc/src/lib.rs | 12 +++++++- kernel/crates/rust-slabmalloc/src/pages.rs | 14 +++++++++ kernel/crates/rust-slabmalloc/src/sc.rs | 34 ++++++++++++++++++--- kernel/crates/rust-slabmalloc/src/zone.rs | 21 ++++++------- kernel/src/mm/allocator/kernel_allocator.rs | 2 +- kernel/src/mm/allocator/slab.rs | 16 +++++++++- 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/kernel/crates/rust-slabmalloc/src/lib.rs b/kernel/crates/rust-slabmalloc/src/lib.rs index 819fc854..5b995fd9 100644 --- a/kernel/crates/rust-slabmalloc/src/lib.rs +++ b/kernel/crates/rust-slabmalloc/src/lib.rs @@ -65,7 +65,12 @@ pub enum AllocationError { /// Needs to adhere to safety requirements of a rust allocator (see GlobalAlloc et. al.). pub unsafe trait Allocator<'a> { fn allocate(&mut self, layout: Layout) -> Result, AllocationError>; - fn deallocate(&mut self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError>; + unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError>; /// Refill the allocator with a [`ObjectPage`]. /// @@ -77,3 +82,8 @@ pub unsafe trait Allocator<'a> { new_page: &'a mut ObjectPage<'a>, ) -> Result<(), AllocationError>; } + +/// 将slab_page归还Buddy的回调函数 +pub trait CallBack: Send + Sync { + unsafe fn free_slab_page(&self, _: *mut u8, _: usize) {} +} diff --git a/kernel/crates/rust-slabmalloc/src/pages.rs b/kernel/crates/rust-slabmalloc/src/pages.rs index 6d2f6516..1e92eb7e 100644 --- a/kernel/crates/rust-slabmalloc/src/pages.rs +++ b/kernel/crates/rust-slabmalloc/src/pages.rs @@ -255,6 +255,20 @@ pub trait AllocablePage { self.bitfield().clear_bit(idx); 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. diff --git a/kernel/crates/rust-slabmalloc/src/sc.rs b/kernel/crates/rust-slabmalloc/src/sc.rs index fd34eada..a17f5770 100644 --- a/kernel/crates/rust-slabmalloc/src/sc.rs +++ b/kernel/crates/rust-slabmalloc/src/sc.rs @@ -59,21 +59,29 @@ pub struct SCAllocator<'a, P: AllocablePage> { pub(crate) slabs: PageList<'a, P>, /// List of full ObjectPages (everything allocated in these don't need to search them). 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 /// re-use the code in const and non-const functions macro_rules! new_sc_allocator { - ($size:expr) => { + ($size:expr) => {{ + let obj_per_page = cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64); SCAllocator { size: $size, allocation_count: 0, - obj_per_page: cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64), + obj_per_page, empty_slabs: PageList::new(), 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> { @@ -241,6 +249,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { *page.next() = Rawlink::none(); trace!("adding page to SCAllocator {:p}", page); self.insert_empty(page); + self.free_obj_count += self.obj_per_page; } /// Allocates a block of memory descriped by `layout`. @@ -294,6 +303,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { self.size, ptr as usize ); + self.free_obj_count -= 1; } res @@ -304,7 +314,12 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { /// May return an error in case an invalid `layout` is provided. /// The function may also move internal slab pages between lists partial -> empty /// or full -> partial lists. - pub fn deallocate(&self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError> { + pub unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError> { assert!(layout.size() <= self.size); assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD)); trace!( @@ -324,6 +339,17 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { let ret = slab_page.deallocate(ptr, new_layout); 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 } } diff --git a/kernel/crates/rust-slabmalloc/src/zone.rs b/kernel/crates/rust-slabmalloc/src/zone.rs index 118d45c0..3e6d7cad 100644 --- a/kernel/crates/rust-slabmalloc/src/zone.rs +++ b/kernel/crates/rust-slabmalloc/src/zone.rs @@ -3,7 +3,6 @@ //! The ZoneAllocator achieves this by having many `SCAllocator` use crate::*; -use core::sync::atomic::Ordering; /// Creates an instance of a zone, we do this in a macro because we /// re-use the code in const and non-const functions @@ -139,16 +138,8 @@ impl<'a> ZoneAllocator<'a> { // 遍历scallocator中的部分分配的page(partial_page) 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的大小,即空闲空间 - free += free_obj_count * scallocator.size(); + free += slab_page.free_obj_count() * scallocator.size(); } // 遍历scallocator中的empty_page,把空页空间也加上去 free += @@ -178,9 +169,15 @@ unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> { /// # Arguments /// * `ptr` - Address of the memory location to free. /// * `layout` - Memory layout of the block pointed to by `ptr`. - fn deallocate(&mut self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError> { + /// * `slab_callback` - The callback function to free slab_page in buddy. + unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError> { 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), } } diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index 6201fef3..9d71d907 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -51,7 +51,7 @@ impl KernelAllocator { 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的幂向上取整。 let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two(); let page_frame_count = PageFrameCount::new(count); diff --git a/kernel/src/mm/allocator/slab.rs b/kernel/src/mm/allocator/slab.rs index 710f9b7b..e564c325 100644 --- a/kernel/src/mm/allocator/slab.rs +++ b/kernel/src/mm/allocator/slab.rs @@ -4,12 +4,16 @@ use alloc::boxed::Box; use log::debug; use slabmalloc::*; +use crate::{arch::MMArch, mm::MemoryManagementArch, KERNEL_ALLOCATOR}; + // 全局slab分配器 pub(crate) static mut SLABALLOCATOR: Option = None; // slab初始化状态 pub(crate) static mut SLABINITSTATE: AtomicBool = AtomicBool::new(false); +static SLAB_CALLBACK: SlabCallback = SlabCallback; + /// slab分配器,实际为一堆小的allocator,可以在里面装4K的page /// 利用这些allocator可以为对象分配不同大小的空间 pub(crate) struct SlabAllocator { @@ -52,7 +56,7 @@ impl SlabAllocator { ) -> Result<(), AllocationError> { if let Some(nptr) = NonNull::new(ptr) { self.zone - .deallocate(nptr, layout) + .deallocate(nptr, layout, &SLAB_CALLBACK) .expect("Couldn't deallocate"); return Ok(()); } else { @@ -80,3 +84,13 @@ pub unsafe fn slab_usage() -> SlabUsage { 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)); + } +}