zone.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. use super::free_area::FreeArea;
  2. use crate::{BuddyPFNOps as _, BuddyRawPage};
  3. use core::sync::atomic::Ordering;
  4. use eonix_mm::{
  5. address::{AddrOps as _, PAddr},
  6. paging::PFN,
  7. };
  8. pub(super) struct Zone<T, const AREAS: usize> {
  9. free_areas: [FreeArea<T>; AREAS],
  10. }
  11. impl<Raw, const AREAS: usize> Zone<Raw, AREAS>
  12. where
  13. Raw: BuddyRawPage,
  14. {
  15. pub const fn new() -> Self {
  16. Self {
  17. free_areas: [const { FreeArea::new() }; AREAS],
  18. }
  19. }
  20. pub fn get_free_pages(&mut self, order: u32) -> Option<Raw> {
  21. for current_order in order..AREAS as u32 {
  22. let pages_ptr = self.free_areas[current_order as usize].get_free_pages();
  23. let Some(pages_ptr) = pages_ptr else { continue };
  24. pages_ptr.set_order(order);
  25. if current_order > order {
  26. self.expand(pages_ptr, current_order, order);
  27. }
  28. assert!(
  29. pages_ptr.is_present(),
  30. "Page {:?} is not present",
  31. pages_ptr.into(),
  32. );
  33. assert!(
  34. pages_ptr.is_free(),
  35. "Page {:?} is not free",
  36. pages_ptr.into(),
  37. );
  38. return Some(pages_ptr);
  39. }
  40. None
  41. }
  42. fn expand(&mut self, pages_ptr: Raw, order: u32, target_order: u32) {
  43. let mut offset = 1 << order;
  44. let pages_pfn = Into::<PFN>::into(pages_ptr);
  45. for order in (target_order..order).rev() {
  46. offset >>= 1;
  47. let split_pages_ptr = Raw::from(pages_pfn + offset);
  48. split_pages_ptr.set_order(order);
  49. split_pages_ptr.set_buddy();
  50. self.free_areas[order as usize].add_pages(split_pages_ptr);
  51. }
  52. }
  53. pub fn free_pages(&mut self, mut pages_ptr: Raw) {
  54. assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
  55. let mut pfn = Into::<PFN>::into(pages_ptr);
  56. let mut current_order = pages_ptr.order();
  57. assert!(
  58. pages_ptr.is_present(),
  59. "Freeing a page that is not present: {:?}",
  60. pages_ptr.into(),
  61. );
  62. assert!(
  63. !pages_ptr.is_free(),
  64. "Freeing a page that is free: {:?}",
  65. pages_ptr.into(),
  66. );
  67. while current_order < (AREAS - 1) as u32 {
  68. let buddy_pfn = pfn.buddy_pfn(current_order);
  69. let buddy_pages_ptr = Raw::from(buddy_pfn);
  70. if !self.buddy_check(buddy_pages_ptr, current_order) {
  71. break;
  72. }
  73. pages_ptr.clear_buddy();
  74. buddy_pages_ptr.clear_buddy();
  75. self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
  76. pages_ptr = Raw::from(pfn.combined_pfn(buddy_pfn));
  77. pfn = pfn.combined_pfn(buddy_pfn);
  78. pages_ptr.set_buddy();
  79. current_order += 1;
  80. }
  81. pages_ptr.set_order(current_order);
  82. self.free_areas[current_order as usize].add_pages(pages_ptr);
  83. }
  84. /// This function checks whether a page is free && is a buddy
  85. /// we can coalesce a page and its buddy if
  86. /// - the buddy is valid(present) &&
  87. /// - the buddy is right now in free_areas &&
  88. /// - a page and its buddy have the same order &&
  89. /// - a page and its buddy are in the same zone (on smp systems).
  90. fn buddy_check(&self, pages_ptr: Raw, order: u32) -> bool {
  91. if !pages_ptr.is_present() {
  92. return false;
  93. }
  94. if !pages_ptr.is_free() {
  95. return false;
  96. }
  97. if pages_ptr.order() != order {
  98. return false;
  99. }
  100. assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
  101. true
  102. }
  103. /// Only used on buddy initialization
  104. pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
  105. let mut start_pfn = PFN::from(start.ceil());
  106. let end_pfn = PFN::from(end.floor());
  107. while start_pfn < end_pfn {
  108. let mut order = usize::from(start_pfn)
  109. .trailing_zeros()
  110. .min((AREAS - 1) as u32);
  111. while start_pfn + (1 << order) as usize > end_pfn {
  112. order -= 1;
  113. }
  114. let page_ptr = Raw::from(start_pfn);
  115. page_ptr.set_buddy();
  116. self.free_areas[order as usize].add_pages(page_ptr);
  117. start_pfn = start_pfn + (1 << order) as usize;
  118. }
  119. }
  120. }