Allocator Designs
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| use alloc::alloc::{GlobalAlloc, Layout}; | use alloc::alloc::{GlobalAlloc, Layout}; | ||||||
| use core::ptr::null_mut; | use core::ptr::null_mut; | ||||||
| use linked_list_allocator::LockedHeap; | use fixed_size_block::FixedSizeBlockAllocator; | ||||||
| use x86_64::{ | use x86_64::{ | ||||||
|     structures::paging::{ |     structures::paging::{ | ||||||
|         mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, |         mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, | ||||||
| @@ -8,11 +8,15 @@ use x86_64::{ | |||||||
|     VirtAddr, |     VirtAddr, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | pub mod bump; | ||||||
|  | pub mod fixed_size_block; | ||||||
|  | pub mod linked_list; | ||||||
|  |  | ||||||
| pub const HEAP_START: usize = 0x_4444_4444_0000; | pub const HEAP_START: usize = 0x_4444_4444_0000; | ||||||
| pub const HEAP_SIZE: usize = 100 * 1024; | pub const HEAP_SIZE: usize = 100 * 1024; | ||||||
|  |  | ||||||
| #[global_allocator] | #[global_allocator] | ||||||
| static ALLOCATOR: LockedHeap = LockedHeap::empty(); | static ALLOCATOR: Locked<FixedSizeBlockAllocator> = Locked::new(FixedSizeBlockAllocator::new()); | ||||||
|  |  | ||||||
| pub fn init_heap( | pub fn init_heap( | ||||||
|     mapper: &mut impl Mapper<Size4KiB>, |     mapper: &mut impl Mapper<Size4KiB>, | ||||||
| @@ -52,3 +56,23 @@ unsafe impl GlobalAlloc for Dummy { | |||||||
|         panic!("dealloc should be never called") |         panic!("dealloc should be never called") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub struct Locked<A> { | ||||||
|  |     inner: spin::Mutex<A>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<A> Locked<A> { | ||||||
|  |     pub const fn new(inner: A) -> Self { | ||||||
|  |         Locked { | ||||||
|  |             inner: spin::Mutex::new(inner), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn lock(&self) -> spin::MutexGuard<A> { | ||||||
|  |         self.inner.lock() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn align_up(addr: usize, align: usize) -> usize { | ||||||
|  |     (addr + align - 1) & !(align - 1) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								src/allocator/bump.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/allocator/bump.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | use super::{align_up, Locked}; | ||||||
|  | use alloc::alloc::{GlobalAlloc, Layout}; | ||||||
|  | use core::ptr; | ||||||
|  |  | ||||||
|  | pub struct BumpAllocator { | ||||||
|  |     heap_start: usize, | ||||||
|  |     heap_end: usize, | ||||||
|  |     next: usize, | ||||||
|  |     allocations: usize, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl BumpAllocator { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         BumpAllocator { | ||||||
|  |             heap_start: 0, | ||||||
|  |             heap_end: 0, | ||||||
|  |             next: 0, | ||||||
|  |             allocations: 0, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { | ||||||
|  |         self.heap_start = heap_start; | ||||||
|  |         self.heap_end = heap_start.saturating_add(heap_size); | ||||||
|  |         self.next = heap_start; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsafe impl GlobalAlloc for Locked<BumpAllocator> { | ||||||
|  |     unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||||||
|  |         let mut bump = self.lock(); | ||||||
|  |  | ||||||
|  |         let alloc_start = align_up(bump.next, layout.align()); | ||||||
|  |         let alloc_end = match alloc_start.checked_add(layout.size()) { | ||||||
|  |             Some(end) => end, | ||||||
|  |             None => return ptr::null_mut(), | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         if alloc_end > bump.heap_end { | ||||||
|  |             ptr::null_mut() | ||||||
|  |         } else { | ||||||
|  |             bump.next = alloc_end; | ||||||
|  |             bump.allocations += 1; | ||||||
|  |             alloc_start as *mut u8 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { | ||||||
|  |         let mut bump = self.lock(); | ||||||
|  |  | ||||||
|  |         bump.allocations -= 1; | ||||||
|  |         if bump.allocations == 0 { | ||||||
|  |             bump.next = bump.heap_start; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								src/allocator/fixed_size_block.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/allocator/fixed_size_block.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | use super::Locked; | ||||||
|  | use alloc::alloc::{GlobalAlloc, Layout}; | ||||||
|  | use core::{ | ||||||
|  |     mem, | ||||||
|  |     ptr::{self, NonNull}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048]; | ||||||
|  |  | ||||||
|  | fn list_index(layout: &Layout) -> Option<usize> { | ||||||
|  |     let required_block_size = layout.size().max(layout.align()); | ||||||
|  |     BLOCK_SIZES.iter().position(|&s| s >= required_block_size) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct ListNode { | ||||||
|  |     next: Option<&'static mut ListNode>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct FixedSizeBlockAllocator { | ||||||
|  |     list_heads: [Option<&'static mut ListNode>; BLOCK_SIZES.len()], | ||||||
|  |     fallback_allocator: linked_list_allocator::Heap, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FixedSizeBlockAllocator { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         const EMPTY: Option<&'static mut ListNode> = None; | ||||||
|  |         FixedSizeBlockAllocator { | ||||||
|  |             list_heads: [EMPTY; BLOCK_SIZES.len()], | ||||||
|  |             fallback_allocator: linked_list_allocator::Heap::empty(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { | ||||||
|  |         self.fallback_allocator.init(heap_start, heap_size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn fallback_alloc(&mut self, layout: Layout) -> *mut u8 { | ||||||
|  |         match self.fallback_allocator.allocate_first_fit(layout) { | ||||||
|  |             Ok(ptr) => ptr.as_ptr(), | ||||||
|  |             Err(_) => ptr::null_mut(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsafe impl GlobalAlloc for Locked<FixedSizeBlockAllocator> { | ||||||
|  |     unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||||||
|  |         let mut allocator = self.lock(); | ||||||
|  |         match list_index(&layout) { | ||||||
|  |             Some(index) => { | ||||||
|  |                 match allocator.list_heads[index].take() { | ||||||
|  |                     Some(node) => { | ||||||
|  |                         allocator.list_heads[index] = node.next.take(); | ||||||
|  |                         node as *mut ListNode as *mut u8 | ||||||
|  |                     } | ||||||
|  |                     None => { | ||||||
|  |                         let block_size = BLOCK_SIZES[index]; | ||||||
|  |                         let block_align = block_size; | ||||||
|  |                         let layout = Layout::from_size_align(block_size, block_align).unwrap(); | ||||||
|  |                         allocator.fallback_alloc(layout) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             None => allocator.fallback_alloc(layout), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { | ||||||
|  |         let mut allocator = self.lock(); | ||||||
|  |         match list_index(&layout) { | ||||||
|  |             Some(index) => { | ||||||
|  |                 let new_node = ListNode { | ||||||
|  |                     next: allocator.list_heads[index].take(), | ||||||
|  |                 }; | ||||||
|  |                 assert!(mem::size_of::<ListNode>() <= BLOCK_SIZES[index]); | ||||||
|  |                 assert!(mem::align_of::<ListNode>() <= BLOCK_SIZES[index]); | ||||||
|  |                 let new_node_ptr = ptr as *mut ListNode; | ||||||
|  |                 new_node_ptr.write(new_node); | ||||||
|  |                 allocator.list_heads[index] = Some(&mut *new_node_ptr); | ||||||
|  |             } | ||||||
|  |             None => { | ||||||
|  |                 let ptr = NonNull::new(ptr).unwrap(); | ||||||
|  |                 allocator.fallback_allocator.deallocate(ptr, layout); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								src/allocator/linked_list.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/allocator/linked_list.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | |||||||
|  | use super::{align_up, Locked}; | ||||||
|  | use alloc::alloc::{GlobalAlloc, Layout}; | ||||||
|  | use core::{mem, ptr}; | ||||||
|  |  | ||||||
|  | struct ListNode { | ||||||
|  |     size: usize, | ||||||
|  |     next: Option<&'static mut ListNode>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl ListNode { | ||||||
|  |     const fn new(size: usize) -> Self { | ||||||
|  |         ListNode { size, next: None } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn start_addr(&self) -> usize { | ||||||
|  |         self as *const Self as usize | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn end_addr(&self) -> usize { | ||||||
|  |         self.start_addr() + self.size | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct LinkedListAllocator { | ||||||
|  |     head: ListNode, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl LinkedListAllocator { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             head: ListNode::new(0), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { | ||||||
|  |         self.add_free_region(heap_start, heap_size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     unsafe fn add_free_region(&mut self, addr: usize, size: usize) { | ||||||
|  |         assert_eq!(align_up(addr, mem::align_of::<ListNode>()), addr); | ||||||
|  |         assert!(size >= mem::size_of::<ListNode>()); | ||||||
|  |  | ||||||
|  |         let mut node = ListNode::new(size); | ||||||
|  |         node.next = self.head.next.take(); | ||||||
|  |         let node_ptr = addr as *mut ListNode; | ||||||
|  |         node_ptr.write(node); | ||||||
|  |         self.head.next = Some(&mut *node_ptr) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn find_region(&mut self, size: usize, align: usize) -> Option<(&'static mut ListNode, usize)> { | ||||||
|  |         let mut current = &mut self.head; | ||||||
|  |         while let Some(ref mut region) = current.next { | ||||||
|  |             if let Ok(alloc_start) = Self::alloc_from_region(®ion, size, align) { | ||||||
|  |                 let next = region.next.take(); | ||||||
|  |                 let ret = Some((current.next.take().unwrap(), alloc_start)); | ||||||
|  |                 current.next = next; | ||||||
|  |                 return ret; | ||||||
|  |             } else { | ||||||
|  |                 current = current.next.as_mut().unwrap(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn alloc_from_region(region: &ListNode, size: usize, align: usize) -> Result<usize, ()> { | ||||||
|  |         let alloc_start = align_up(region.start_addr(), align); | ||||||
|  |         let alloc_end = alloc_start.checked_add(size).ok_or(())?; | ||||||
|  |  | ||||||
|  |         if alloc_end > region.end_addr() { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let excess_size = region.end_addr() - alloc_end; | ||||||
|  |         if excess_size > 0 && excess_size < mem::size_of::<ListNode>() { | ||||||
|  |             return Err(()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(alloc_start) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn size_align(layout: Layout) -> (usize, usize) { | ||||||
|  |         let layout = layout | ||||||
|  |             .align_to(mem::align_of::<ListNode>()) | ||||||
|  |             .expect("adjusting alignment failed") | ||||||
|  |             .pad_to_align(); | ||||||
|  |         let size = layout.size().max(mem::size_of::<ListNode>()); | ||||||
|  |         (size, layout.align()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsafe impl GlobalAlloc for Locked<LinkedListAllocator> { | ||||||
|  |     unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||||||
|  |         let (size, align) = LinkedListAllocator::size_align(layout); | ||||||
|  |         let mut allocator = self.lock(); | ||||||
|  |  | ||||||
|  |         if let Some((region, alloc_start)) = allocator.find_region(size, align) { | ||||||
|  |             let alloc_end = alloc_start.checked_add(size).expect("overflow"); | ||||||
|  |             let excess_size = region.end_addr() - alloc_end; | ||||||
|  |             if excess_size > 0 { | ||||||
|  |                 allocator.add_free_region(alloc_end, excess_size); | ||||||
|  |             } | ||||||
|  |             alloc_start as *mut u8 | ||||||
|  |         } else { | ||||||
|  |             ptr::null_mut() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { | ||||||
|  |         let (size, _) = LinkedListAllocator::size_align(layout); | ||||||
|  |  | ||||||
|  |         self.lock().add_free_region(ptr as usize, size) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,10 +1,11 @@ | |||||||
| #![no_std] | #![no_std] | ||||||
| #![cfg_attr(test, no_main)] | #![cfg_attr(test, no_main)] | ||||||
| #![feature(custom_test_frameworks)] | #![feature(custom_test_frameworks)] | ||||||
| #![test_runner(crate::test_runner)] |  | ||||||
| #![reexport_test_harness_main = "test_main"] |  | ||||||
| #![feature(abi_x86_interrupt)] | #![feature(abi_x86_interrupt)] | ||||||
| #![feature(alloc_error_handler)] | #![feature(alloc_error_handler)] | ||||||
|  | #![feature(const_mut_refs)] | ||||||
|  | #![test_runner(crate::test_runner)] | ||||||
|  | #![reexport_test_harness_main = "test_main"] | ||||||
|  |  | ||||||
| extern crate alloc; | extern crate alloc; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,6 +54,16 @@ fn many_boxes() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test_case] | ||||||
|  | fn many_boxes_long_lived() { | ||||||
|  |     let long_lived = Box::new(1); | ||||||
|  |     for i in 0..HEAP_SIZE { | ||||||
|  |         let x = Box::new(i); | ||||||
|  |         assert_eq!(*x, i); | ||||||
|  |     } | ||||||
|  |     assert_eq!(*long_lived, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
| #[panic_handler] | #[panic_handler] | ||||||
| fn panic(info: &PanicInfo) -> ! { | fn panic(info: &PanicInfo) -> ! { | ||||||
|     anos::test_panic_handler(info) |     anos::test_panic_handler(info) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user