sc.rs 13 KB

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