1*ceeb2e94Slaokengwt //! A SCAllocator that can allocate fixed size objects. 2*ceeb2e94Slaokengwt 3*ceeb2e94Slaokengwt use core::mem; 4*ceeb2e94Slaokengwt 5*ceeb2e94Slaokengwt use crate::*; 6*ceeb2e94Slaokengwt 7*ceeb2e94Slaokengwt /// A genius(?) const min() 8*ceeb2e94Slaokengwt /// 9*ceeb2e94Slaokengwt /// # What this does 10*ceeb2e94Slaokengwt /// * create an array of the two elements you want to choose between 11*ceeb2e94Slaokengwt /// * create an arbitrary boolean expression 12*ceeb2e94Slaokengwt /// * cast said expresison to a usize 13*ceeb2e94Slaokengwt /// * use that value to index into the array created above 14*ceeb2e94Slaokengwt /// 15*ceeb2e94Slaokengwt /// # Source 16*ceeb2e94Slaokengwt /// https://stackoverflow.com/questions/53619695/calculating-maximum-value-of-a-set-of-constant-expressions-at-compile-time 17*ceeb2e94Slaokengwt #[cfg(feature = "unstable")] 18*ceeb2e94Slaokengwt const fn cmin(a: usize, b: usize) -> usize { 19*ceeb2e94Slaokengwt [a, b][(a > b) as usize] 20*ceeb2e94Slaokengwt } 21*ceeb2e94Slaokengwt 22*ceeb2e94Slaokengwt /// The boring variant of min (not const). 23*ceeb2e94Slaokengwt #[cfg(not(feature = "unstable"))] 24*ceeb2e94Slaokengwt fn cmin(a: usize, b: usize) -> usize { 25*ceeb2e94Slaokengwt core::cmp::min(a, b) 26*ceeb2e94Slaokengwt } 27*ceeb2e94Slaokengwt 28*ceeb2e94Slaokengwt /// A slab allocator allocates elements of a fixed size. 29*ceeb2e94Slaokengwt /// 30*ceeb2e94Slaokengwt /// It maintains three internal lists of objects that implement `AllocablePage` 31*ceeb2e94Slaokengwt /// from which it can allocate memory. 32*ceeb2e94Slaokengwt /// 33*ceeb2e94Slaokengwt /// * `empty_slabs`: Is a list of pages that the SCAllocator maintains, but 34*ceeb2e94Slaokengwt /// has 0 allocations in them, these can be given back to a requestor in case 35*ceeb2e94Slaokengwt /// of reclamation. 36*ceeb2e94Slaokengwt /// * `slabs`: A list of pages partially allocated and still have room for more. 37*ceeb2e94Slaokengwt /// * `full_slabs`: A list of pages that are completely allocated. 38*ceeb2e94Slaokengwt /// 39*ceeb2e94Slaokengwt /// On allocation we allocate memory from `slabs`, however if the list is empty 40*ceeb2e94Slaokengwt /// we try to reclaim a page from `empty_slabs` before we return with an out-of-memory 41*ceeb2e94Slaokengwt /// error. If a page becomes full after the allocation we move it from `slabs` to 42*ceeb2e94Slaokengwt /// `full_slabs`. 43*ceeb2e94Slaokengwt /// 44*ceeb2e94Slaokengwt /// Similarly, on dealloaction we might move a page from `full_slabs` to `slabs` 45*ceeb2e94Slaokengwt /// or from `slabs` to `empty_slabs` after we deallocated an object. 46*ceeb2e94Slaokengwt /// 47*ceeb2e94Slaokengwt /// If an allocation returns `OutOfMemory` a client using SCAllocator can refill 48*ceeb2e94Slaokengwt /// it using the `refill` function. 49*ceeb2e94Slaokengwt pub struct SCAllocator<'a, P: AllocablePage> { 50*ceeb2e94Slaokengwt /// Maximum possible allocation size for this `SCAllocator`. 51*ceeb2e94Slaokengwt pub(crate) size: usize, 52*ceeb2e94Slaokengwt /// Keeps track of succeeded allocations. 53*ceeb2e94Slaokengwt pub(crate) allocation_count: usize, 54*ceeb2e94Slaokengwt /// max objects per page 55*ceeb2e94Slaokengwt pub(crate) obj_per_page: usize, 56*ceeb2e94Slaokengwt /// List of empty ObjectPages (nothing allocated in these). 57*ceeb2e94Slaokengwt pub(crate) empty_slabs: PageList<'a, P>, 58*ceeb2e94Slaokengwt /// List of partially used ObjectPage (some objects allocated but pages are not full). 59*ceeb2e94Slaokengwt pub(crate) slabs: PageList<'a, P>, 60*ceeb2e94Slaokengwt /// List of full ObjectPages (everything allocated in these don't need to search them). 61*ceeb2e94Slaokengwt pub(crate) full_slabs: PageList<'a, P>, 62*ceeb2e94Slaokengwt } 63*ceeb2e94Slaokengwt 64*ceeb2e94Slaokengwt /// Creates an instance of a scallocator, we do this in a macro because we 65*ceeb2e94Slaokengwt /// re-use the code in const and non-const functions 66*ceeb2e94Slaokengwt macro_rules! new_sc_allocator { 67*ceeb2e94Slaokengwt ($size:expr) => { 68*ceeb2e94Slaokengwt SCAllocator { 69*ceeb2e94Slaokengwt size: $size, 70*ceeb2e94Slaokengwt allocation_count: 0, 71*ceeb2e94Slaokengwt obj_per_page: cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64), 72*ceeb2e94Slaokengwt empty_slabs: PageList::new(), 73*ceeb2e94Slaokengwt slabs: PageList::new(), 74*ceeb2e94Slaokengwt full_slabs: PageList::new(), 75*ceeb2e94Slaokengwt } 76*ceeb2e94Slaokengwt }; 77*ceeb2e94Slaokengwt } 78*ceeb2e94Slaokengwt 79*ceeb2e94Slaokengwt impl<'a, P: AllocablePage> SCAllocator<'a, P> { 80*ceeb2e94Slaokengwt const REBALANCE_COUNT: usize = 64; 81*ceeb2e94Slaokengwt 82*ceeb2e94Slaokengwt /// Create a new SCAllocator. 83*ceeb2e94Slaokengwt #[cfg(feature = "unstable")] 84*ceeb2e94Slaokengwt pub const fn new(size: usize) -> SCAllocator<'a, P> { 85*ceeb2e94Slaokengwt new_sc_allocator!(size) 86*ceeb2e94Slaokengwt } 87*ceeb2e94Slaokengwt 88*ceeb2e94Slaokengwt #[cfg(not(feature = "unstable"))] 89*ceeb2e94Slaokengwt pub fn new(size: usize) -> SCAllocator<'a, P> { 90*ceeb2e94Slaokengwt new_sc_allocator!(size) 91*ceeb2e94Slaokengwt } 92*ceeb2e94Slaokengwt 93*ceeb2e94Slaokengwt /// Returns the maximum supported object size of this allocator. 94*ceeb2e94Slaokengwt pub fn size(&self) -> usize { 95*ceeb2e94Slaokengwt self.size 96*ceeb2e94Slaokengwt } 97*ceeb2e94Slaokengwt 98*ceeb2e94Slaokengwt /// Add a new ObjectPage. 99*ceeb2e94Slaokengwt fn insert_partial_slab(&mut self, new_head: &'a mut P) { 100*ceeb2e94Slaokengwt self.slabs.insert_front(new_head); 101*ceeb2e94Slaokengwt } 102*ceeb2e94Slaokengwt 103*ceeb2e94Slaokengwt /// Add page to empty list. 104*ceeb2e94Slaokengwt fn insert_empty(&mut self, new_head: &'a mut P) { 105*ceeb2e94Slaokengwt assert_eq!( 106*ceeb2e94Slaokengwt new_head as *const P as usize % P::SIZE, 107*ceeb2e94Slaokengwt 0, 108*ceeb2e94Slaokengwt "Inserted page is not aligned to page-size." 109*ceeb2e94Slaokengwt ); 110*ceeb2e94Slaokengwt self.empty_slabs.insert_front(new_head); 111*ceeb2e94Slaokengwt } 112*ceeb2e94Slaokengwt 113*ceeb2e94Slaokengwt /// Since `dealloc` can not reassign pages without requiring a lock 114*ceeb2e94Slaokengwt /// we check slabs and full slabs periodically as part of `alloc` 115*ceeb2e94Slaokengwt /// and move them to the empty or partially allocated slab lists. 116*ceeb2e94Slaokengwt pub(crate) fn check_page_assignments(&mut self) { 117*ceeb2e94Slaokengwt for slab_page in self.full_slabs.iter_mut() { 118*ceeb2e94Slaokengwt if !slab_page.is_full() { 119*ceeb2e94Slaokengwt // We need to move it from self.full_slabs -> self.slabs 120*ceeb2e94Slaokengwt trace!("move {:p} full -> partial", slab_page); 121*ceeb2e94Slaokengwt self.move_full_to_partial(slab_page); 122*ceeb2e94Slaokengwt } 123*ceeb2e94Slaokengwt } 124*ceeb2e94Slaokengwt 125*ceeb2e94Slaokengwt for slab_page in self.slabs.iter_mut() { 126*ceeb2e94Slaokengwt if slab_page.is_empty(self.obj_per_page) { 127*ceeb2e94Slaokengwt // We need to move it from self.slabs -> self.empty_slabs 128*ceeb2e94Slaokengwt trace!("move {:p} partial -> empty", slab_page); 129*ceeb2e94Slaokengwt self.move_to_empty(slab_page); 130*ceeb2e94Slaokengwt } 131*ceeb2e94Slaokengwt } 132*ceeb2e94Slaokengwt } 133*ceeb2e94Slaokengwt 134*ceeb2e94Slaokengwt /// Move a page from `slabs` to `empty_slabs`. 135*ceeb2e94Slaokengwt fn move_to_empty(&mut self, page: &'a mut P) { 136*ceeb2e94Slaokengwt let page_ptr = page as *const P; 137*ceeb2e94Slaokengwt 138*ceeb2e94Slaokengwt debug_assert!(self.slabs.contains(page_ptr)); 139*ceeb2e94Slaokengwt debug_assert!( 140*ceeb2e94Slaokengwt !self.empty_slabs.contains(page_ptr), 141*ceeb2e94Slaokengwt "Page {:p} already in emtpy_slabs", 142*ceeb2e94Slaokengwt page_ptr 143*ceeb2e94Slaokengwt ); 144*ceeb2e94Slaokengwt 145*ceeb2e94Slaokengwt self.slabs.remove_from_list(page); 146*ceeb2e94Slaokengwt self.empty_slabs.insert_front(page); 147*ceeb2e94Slaokengwt 148*ceeb2e94Slaokengwt debug_assert!(!self.slabs.contains(page_ptr)); 149*ceeb2e94Slaokengwt debug_assert!(self.empty_slabs.contains(page_ptr)); 150*ceeb2e94Slaokengwt } 151*ceeb2e94Slaokengwt 152*ceeb2e94Slaokengwt /// Move a page from `full_slabs` to `slab`. 153*ceeb2e94Slaokengwt fn move_partial_to_full(&mut self, page: &'a mut P) { 154*ceeb2e94Slaokengwt let page_ptr = page as *const P; 155*ceeb2e94Slaokengwt 156*ceeb2e94Slaokengwt debug_assert!(self.slabs.contains(page_ptr)); 157*ceeb2e94Slaokengwt debug_assert!(!self.full_slabs.contains(page_ptr)); 158*ceeb2e94Slaokengwt 159*ceeb2e94Slaokengwt self.slabs.remove_from_list(page); 160*ceeb2e94Slaokengwt self.full_slabs.insert_front(page); 161*ceeb2e94Slaokengwt 162*ceeb2e94Slaokengwt debug_assert!(!self.slabs.contains(page_ptr)); 163*ceeb2e94Slaokengwt debug_assert!(self.full_slabs.contains(page_ptr)); 164*ceeb2e94Slaokengwt } 165*ceeb2e94Slaokengwt 166*ceeb2e94Slaokengwt /// Move a page from `full_slabs` to `slab`. 167*ceeb2e94Slaokengwt fn move_full_to_partial(&mut self, page: &'a mut P) { 168*ceeb2e94Slaokengwt let page_ptr = page as *const P; 169*ceeb2e94Slaokengwt 170*ceeb2e94Slaokengwt debug_assert!(!self.slabs.contains(page_ptr)); 171*ceeb2e94Slaokengwt debug_assert!(self.full_slabs.contains(page_ptr)); 172*ceeb2e94Slaokengwt 173*ceeb2e94Slaokengwt self.full_slabs.remove_from_list(page); 174*ceeb2e94Slaokengwt self.slabs.insert_front(page); 175*ceeb2e94Slaokengwt 176*ceeb2e94Slaokengwt debug_assert!(self.slabs.contains(page_ptr)); 177*ceeb2e94Slaokengwt debug_assert!(!self.full_slabs.contains(page_ptr)); 178*ceeb2e94Slaokengwt } 179*ceeb2e94Slaokengwt 180*ceeb2e94Slaokengwt /// Tries to allocate a block of memory with respect to the `layout`. 181*ceeb2e94Slaokengwt /// Searches within already allocated slab pages, if no suitable spot is found 182*ceeb2e94Slaokengwt /// will try to use a page from the empty page list. 183*ceeb2e94Slaokengwt /// 184*ceeb2e94Slaokengwt /// # Arguments 185*ceeb2e94Slaokengwt /// * `sc_layout`: This is not the original layout but adjusted for the 186*ceeb2e94Slaokengwt /// SCAllocator size (>= original). 187*ceeb2e94Slaokengwt fn try_allocate_from_pagelist(&mut self, sc_layout: Layout) -> *mut u8 { 188*ceeb2e94Slaokengwt // TODO: Do we really need to check multiple slab pages (due to alignment) 189*ceeb2e94Slaokengwt // If not we can get away with a singly-linked list and have 8 more bytes 190*ceeb2e94Slaokengwt // for the bitfield in an ObjectPage. 191*ceeb2e94Slaokengwt 192*ceeb2e94Slaokengwt for slab_page in self.slabs.iter_mut() { 193*ceeb2e94Slaokengwt let ptr = slab_page.allocate(sc_layout); 194*ceeb2e94Slaokengwt if !ptr.is_null() { 195*ceeb2e94Slaokengwt if slab_page.is_full() { 196*ceeb2e94Slaokengwt trace!("move {:p} partial -> full", slab_page); 197*ceeb2e94Slaokengwt self.move_partial_to_full(slab_page); 198*ceeb2e94Slaokengwt } 199*ceeb2e94Slaokengwt self.allocation_count += 1; 200*ceeb2e94Slaokengwt return ptr; 201*ceeb2e94Slaokengwt } else { 202*ceeb2e94Slaokengwt continue; 203*ceeb2e94Slaokengwt } 204*ceeb2e94Slaokengwt } 205*ceeb2e94Slaokengwt 206*ceeb2e94Slaokengwt // Periodically rebalance page-lists (since dealloc can't do it for us) 207*ceeb2e94Slaokengwt if self.allocation_count > SCAllocator::<P>::REBALANCE_COUNT { 208*ceeb2e94Slaokengwt self.check_page_assignments(); 209*ceeb2e94Slaokengwt self.allocation_count = 0; 210*ceeb2e94Slaokengwt } 211*ceeb2e94Slaokengwt 212*ceeb2e94Slaokengwt ptr::null_mut() 213*ceeb2e94Slaokengwt } 214*ceeb2e94Slaokengwt 215*ceeb2e94Slaokengwt pub fn try_reclaim_pages<F>(&mut self, to_reclaim: usize, dealloc: &mut F) -> usize 216*ceeb2e94Slaokengwt where 217*ceeb2e94Slaokengwt F: FnMut(*mut P), 218*ceeb2e94Slaokengwt { 219*ceeb2e94Slaokengwt self.check_page_assignments(); 220*ceeb2e94Slaokengwt let mut reclaimed = 0; 221*ceeb2e94Slaokengwt while reclaimed < to_reclaim { 222*ceeb2e94Slaokengwt if let Some(page) = self.empty_slabs.pop() { 223*ceeb2e94Slaokengwt dealloc(page as *mut P); 224*ceeb2e94Slaokengwt reclaimed += 1; 225*ceeb2e94Slaokengwt } else { 226*ceeb2e94Slaokengwt break; 227*ceeb2e94Slaokengwt } 228*ceeb2e94Slaokengwt } 229*ceeb2e94Slaokengwt 230*ceeb2e94Slaokengwt reclaimed 231*ceeb2e94Slaokengwt } 232*ceeb2e94Slaokengwt 233*ceeb2e94Slaokengwt /// Refill the SCAllocator 234*ceeb2e94Slaokengwt /// 235*ceeb2e94Slaokengwt /// # Safety 236*ceeb2e94Slaokengwt /// ObjectPage needs to be empty etc. 237*ceeb2e94Slaokengwt pub unsafe fn refill(&mut self, page: &'a mut P) { 238*ceeb2e94Slaokengwt page.bitfield_mut() 239*ceeb2e94Slaokengwt .initialize(self.size, P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD); 240*ceeb2e94Slaokengwt *page.prev() = Rawlink::none(); 241*ceeb2e94Slaokengwt *page.next() = Rawlink::none(); 242*ceeb2e94Slaokengwt trace!("adding page to SCAllocator {:p}", page); 243*ceeb2e94Slaokengwt self.insert_empty(page); 244*ceeb2e94Slaokengwt } 245*ceeb2e94Slaokengwt 246*ceeb2e94Slaokengwt /// Allocates a block of memory descriped by `layout`. 247*ceeb2e94Slaokengwt /// 248*ceeb2e94Slaokengwt /// Returns a pointer to a valid region of memory or an 249*ceeb2e94Slaokengwt /// AllocationError. 250*ceeb2e94Slaokengwt /// 251*ceeb2e94Slaokengwt /// The function may also move around pages between lists 252*ceeb2e94Slaokengwt /// (empty -> partial or partial -> full). 253*ceeb2e94Slaokengwt pub fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> { 254*ceeb2e94Slaokengwt trace!( 255*ceeb2e94Slaokengwt "SCAllocator({}) is trying to allocate {:?}", 256*ceeb2e94Slaokengwt self.size, 257*ceeb2e94Slaokengwt layout 258*ceeb2e94Slaokengwt ); 259*ceeb2e94Slaokengwt assert!(layout.size() <= self.size); 260*ceeb2e94Slaokengwt assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD)); 261*ceeb2e94Slaokengwt let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) }; 262*ceeb2e94Slaokengwt assert!(new_layout.size() >= layout.size()); 263*ceeb2e94Slaokengwt 264*ceeb2e94Slaokengwt let ptr = { 265*ceeb2e94Slaokengwt // Try to allocate from partial slabs, 266*ceeb2e94Slaokengwt // if we fail check if we have empty pages and allocate from there 267*ceeb2e94Slaokengwt let ptr = self.try_allocate_from_pagelist(new_layout); 268*ceeb2e94Slaokengwt if ptr.is_null() && self.empty_slabs.head.is_some() { 269*ceeb2e94Slaokengwt // Re-try allocation in empty page 270*ceeb2e94Slaokengwt let empty_page = self.empty_slabs.pop().expect("We checked head.is_some()"); 271*ceeb2e94Slaokengwt debug_assert!(!self.empty_slabs.contains(empty_page)); 272*ceeb2e94Slaokengwt 273*ceeb2e94Slaokengwt let ptr = empty_page.allocate(layout); 274*ceeb2e94Slaokengwt debug_assert!(!ptr.is_null(), "Allocation must have succeeded here."); 275*ceeb2e94Slaokengwt 276*ceeb2e94Slaokengwt trace!( 277*ceeb2e94Slaokengwt "move {:p} empty -> partial empty count {}", 278*ceeb2e94Slaokengwt empty_page, 279*ceeb2e94Slaokengwt self.empty_slabs.elements 280*ceeb2e94Slaokengwt ); 281*ceeb2e94Slaokengwt // Move empty page to partial pages 282*ceeb2e94Slaokengwt self.insert_partial_slab(empty_page); 283*ceeb2e94Slaokengwt ptr 284*ceeb2e94Slaokengwt } else { 285*ceeb2e94Slaokengwt ptr 286*ceeb2e94Slaokengwt } 287*ceeb2e94Slaokengwt }; 288*ceeb2e94Slaokengwt 289*ceeb2e94Slaokengwt let res = NonNull::new(ptr).ok_or(AllocationError::OutOfMemory); 290*ceeb2e94Slaokengwt 291*ceeb2e94Slaokengwt if !ptr.is_null() { 292*ceeb2e94Slaokengwt trace!( 293*ceeb2e94Slaokengwt "SCAllocator({}) allocated ptr=0x{:x}", 294*ceeb2e94Slaokengwt self.size, 295*ceeb2e94Slaokengwt ptr as usize 296*ceeb2e94Slaokengwt ); 297*ceeb2e94Slaokengwt } 298*ceeb2e94Slaokengwt 299*ceeb2e94Slaokengwt res 300*ceeb2e94Slaokengwt } 301*ceeb2e94Slaokengwt 302*ceeb2e94Slaokengwt /// Deallocates a previously allocated `ptr` described by `Layout`. 303*ceeb2e94Slaokengwt /// 304*ceeb2e94Slaokengwt /// May return an error in case an invalid `layout` is provided. 305*ceeb2e94Slaokengwt /// The function may also move internal slab pages between lists partial -> empty 306*ceeb2e94Slaokengwt /// or full -> partial lists. 307*ceeb2e94Slaokengwt pub fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> { 308*ceeb2e94Slaokengwt assert!(layout.size() <= self.size); 309*ceeb2e94Slaokengwt assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD)); 310*ceeb2e94Slaokengwt trace!( 311*ceeb2e94Slaokengwt "SCAllocator({}) is trying to deallocate ptr = {:p} layout={:?} P.size= {}", 312*ceeb2e94Slaokengwt self.size, 313*ceeb2e94Slaokengwt ptr, 314*ceeb2e94Slaokengwt layout, 315*ceeb2e94Slaokengwt P::SIZE 316*ceeb2e94Slaokengwt ); 317*ceeb2e94Slaokengwt 318*ceeb2e94Slaokengwt let page = (ptr.as_ptr() as usize) & !(P::SIZE - 1); 319*ceeb2e94Slaokengwt 320*ceeb2e94Slaokengwt // Figure out which page we are on and construct a reference to it 321*ceeb2e94Slaokengwt // TODO: The linked list will have another &mut reference 322*ceeb2e94Slaokengwt let slab_page = unsafe { mem::transmute::<VAddr, &'a mut P>(page) }; 323*ceeb2e94Slaokengwt let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) }; 324*ceeb2e94Slaokengwt 325*ceeb2e94Slaokengwt let ret = slab_page.deallocate(ptr, new_layout); 326*ceeb2e94Slaokengwt debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment"); 327*ceeb2e94Slaokengwt ret 328*ceeb2e94Slaokengwt } 329*ceeb2e94Slaokengwt } 330