zone.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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!(pages_ptr.is_free() && pages_ptr.is_present());
  29. return Some(pages_ptr);
  30. }
  31. None
  32. }
  33. fn expand(&mut self, pages_ptr: Raw, order: u32, target_order: u32) {
  34. let mut offset = 1 << order;
  35. let pages_pfn = Into::<PFN>::into(pages_ptr);
  36. for order in (target_order..order).rev() {
  37. offset >>= 1;
  38. let split_pages_ptr = Raw::from(pages_pfn + offset);
  39. split_pages_ptr.set_order(order);
  40. split_pages_ptr.set_buddy();
  41. self.free_areas[order as usize].add_pages(split_pages_ptr);
  42. }
  43. }
  44. pub fn free_pages(&mut self, mut pages_ptr: Raw) {
  45. assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
  46. let mut pfn = Into::<PFN>::into(pages_ptr);
  47. let mut current_order = pages_ptr.order();
  48. while current_order < (AREAS - 1) as u32 {
  49. let buddy_pfn = pfn.buddy_pfn(current_order);
  50. let buddy_pages_ptr = Raw::from(buddy_pfn);
  51. if !self.buddy_check(buddy_pages_ptr, current_order) {
  52. break;
  53. }
  54. pages_ptr.clear_buddy();
  55. buddy_pages_ptr.clear_buddy();
  56. self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
  57. pages_ptr = Raw::from(pfn.combined_pfn(buddy_pfn));
  58. pfn = pfn.combined_pfn(buddy_pfn);
  59. pages_ptr.set_buddy();
  60. current_order += 1;
  61. }
  62. pages_ptr.set_order(current_order);
  63. self.free_areas[current_order as usize].add_pages(pages_ptr);
  64. }
  65. /// This function checks whether a page is free && is a buddy
  66. /// we can coalesce a page and its buddy if
  67. /// - the buddy is valid(present) &&
  68. /// - the buddy is right now in free_areas &&
  69. /// - a page and its buddy have the same order &&
  70. /// - a page and its buddy are in the same zone (on smp systems).
  71. fn buddy_check(&self, pages_ptr: Raw, order: u32) -> bool {
  72. if !pages_ptr.is_present() {
  73. return false;
  74. }
  75. if !pages_ptr.is_free() {
  76. return false;
  77. }
  78. if pages_ptr.order() != order {
  79. return false;
  80. }
  81. assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
  82. true
  83. }
  84. /// Only used on buddy initialization
  85. pub fn create_pages(&mut self, start: PAddr, end: PAddr) {
  86. let mut start_pfn = PFN::from(start.ceil());
  87. let end_pfn = PFN::from(end.floor());
  88. while start_pfn < end_pfn {
  89. let mut order = usize::from(start_pfn)
  90. .trailing_zeros()
  91. .min((AREAS - 1) as u32);
  92. while start_pfn + order as usize > end_pfn {
  93. order -= 1;
  94. }
  95. let page_ptr = Raw::from(start_pfn);
  96. page_ptr.set_buddy();
  97. self.free_areas[order as usize].add_pages(page_ptr);
  98. start_pfn = start_pfn + (1 << order) as usize;
  99. }
  100. }
  101. }