فهرست منبع

change(page_alloc): better abstraction

greatbridf 8 ماه پیش
والد
کامیت
676089587c

+ 1 - 3
Cargo.lock

@@ -107,9 +107,6 @@ dependencies = [
 [[package]]
 name = "eonix_mm"
 version = "0.1.0"
-dependencies = [
- "intrusive_list",
-]
 
 [[package]]
 name = "eonix_preempt"
@@ -155,6 +152,7 @@ dependencies = [
  "eonix_runtime",
  "eonix_sync",
  "intrusive-collections",
+ "intrusive_list",
  "itertools",
  "pointers",
  "posix_types",

+ 1 - 0
Cargo.toml

@@ -17,6 +17,7 @@ eonix_preempt = { path = "./crates/eonix_preempt" }
 eonix_runtime = { path = "./crates/eonix_runtime" }
 eonix_sync = { path = "./crates/eonix_sync" }
 eonix_log = { path = "./crates/eonix_log" }
+intrusive_list = { path = "./crates/intrusive_list" }
 pointers = { path = "./crates/pointers" }
 posix_types = { path = "./crates/posix_types" }
 

+ 29 - 17
crates/buddy_allocator/src/free_area.rs

@@ -1,29 +1,36 @@
-use core::marker::{Send, Sync};
-use eonix_mm::paging::{PageFlags, RawPage, RawPagePtr};
-use intrusive_list::{container_of, Link};
+use crate::BuddyRawPage;
+use core::marker::{PhantomData, Send, Sync};
+use intrusive_list::Link;
 
-pub struct FreeArea {
+pub struct FreeArea<T> {
     free_list: Link,
     count: usize,
+    _phantom: PhantomData<T>,
 }
 
-unsafe impl Send for FreeArea {}
-unsafe impl Sync for FreeArea {}
+unsafe impl<T> Send for FreeArea<T> {}
+unsafe impl<T> Sync for FreeArea<T> {}
 
-impl FreeArea {
+impl<Raw> FreeArea<Raw>
+where
+    Raw: BuddyRawPage,
+{
     pub const fn new() -> Self {
         Self {
             free_list: Link::new(),
             count: 0,
+            _phantom: PhantomData,
         }
     }
 
-    pub fn get_free_pages(&mut self) -> Option<RawPagePtr> {
+    pub fn get_free_pages(&mut self) -> Option<Raw> {
         self.free_list.next_mut().map(|pages_link| {
             assert_ne!(self.count, 0);
 
-            let pages_ptr = unsafe { container_of!(pages_link, RawPage, link) };
-            let pages_ptr = RawPagePtr::new(pages_ptr);
+            let pages_ptr = unsafe {
+                // SAFETY: Items in `self.free_list` are guaranteed to be of type `Raw`.
+                Raw::from_link(pages_link)
+            };
 
             self.count -= 1;
             pages_link.remove();
@@ -32,16 +39,21 @@ impl FreeArea {
         })
     }
 
-    pub fn add_pages(&mut self, pages_ptr: RawPagePtr) {
+    pub fn add_pages(&mut self, pages_ptr: Raw) {
         self.count += 1;
-        pages_ptr.as_mut().flags.set(PageFlags::FREE);
-        self.free_list.insert(&mut pages_ptr.as_mut().link)
+        pages_ptr.set_free();
+
+        unsafe {
+            self.free_list.insert(pages_ptr.get_link());
+        }
     }
 
-    pub fn del_pages(&mut self, pages_ptr: RawPagePtr) {
-        assert!(self.count >= 1 && pages_ptr.as_ref().flags.has(PageFlags::FREE));
+    pub fn del_pages(&mut self, pages_ptr: Raw) {
+        assert!(self.count >= 1 && pages_ptr.is_free());
         self.count -= 1;
-        pages_ptr.as_mut().flags.clear(PageFlags::FREE);
-        pages_ptr.as_mut().link.remove();
+        pages_ptr.clear_free();
+        unsafe {
+            pages_ptr.get_link().remove();
+        }
     }
 }

+ 52 - 18
crates/buddy_allocator/src/lib.rs

@@ -6,54 +6,88 @@ mod zone;
 use core::sync::atomic::Ordering;
 use eonix_mm::{
     address::PAddr,
-    paging::{PageAlloc, PageFlags, RawPagePtr, PFN},
+    paging::{PageAlloc, RawPage, PFN},
 };
 use eonix_sync::Spin;
+use intrusive_list::Link;
 use zone::Zone;
 
-pub use free_area::FreeArea;
-
 const MAX_ORDER: u32 = 10;
 const ZONE_AREAS: usize = const { MAX_ORDER as usize + 1 };
 
-static BUDDY_ALLOCATOR: BuddyAllocator = BuddyAllocator::new();
+pub trait BuddyRawPage: RawPage {
+    /// Get the container raw page struct of the list link.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that the link points to a `RawPage`.
+    unsafe fn from_link(link: &mut Link) -> Self;
+
+    /// Get the list link of the raw page.
+    ///
+    /// # Safety
+    /// The caller MUST ensure that at any time, only one mutable reference
+    /// to the link exists.
+    unsafe fn get_link(&self) -> &mut Link;
+
+    fn set_order(&self, order: u32);
+
+    fn is_buddy(&self) -> bool;
+    fn is_free(&self) -> bool;
 
-pub struct BuddyAllocator {
-    zone: Spin<Zone<ZONE_AREAS>>,
+    fn set_buddy(&self);
+    fn set_free(&self);
+
+    fn clear_buddy(&self);
+    fn clear_free(&self);
+}
+
+pub struct BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    zone: Spin<Zone<T, ZONE_AREAS>>,
 }
 
-impl BuddyAllocator {
-    const fn new() -> Self {
+impl<T> BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    pub const fn new() -> Self {
         Self {
             zone: Spin::new(Zone::new()),
         }
     }
 
-    pub fn create_pages(start: PAddr, end: PAddr) {
-        BUDDY_ALLOCATOR.zone.lock().create_pages(start, end);
+    pub fn create_pages(&self, start: PAddr, end: PAddr) {
+        self.zone.lock().create_pages(start, end);
     }
 }
 
-impl PageAlloc for BuddyAllocator {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
-        let pages_ptr = BUDDY_ALLOCATOR.zone.lock().get_free_pages(order);
+impl<T> PageAlloc for &'static BuddyAllocator<T>
+where
+    T: BuddyRawPage,
+{
+    type RawPage = T;
+
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
+        let pages_ptr = self.zone.lock().get_free_pages(order);
 
         if let Some(pages_ptr) = pages_ptr {
             // SAFETY: Memory order here can be Relaxed is for the same reason as that
             // in the copy constructor of `std::shared_ptr`.
             pages_ptr.refcount().fetch_add(1, Ordering::Relaxed);
-            pages_ptr.flags().clear(PageFlags::FREE);
+            pages_ptr.clear_free();
         }
 
         pages_ptr
     }
 
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
-        BUDDY_ALLOCATOR.zone.lock().free_pages(page_ptr);
+    unsafe fn dealloc(&self, page_ptr: Self::RawPage) {
+        self.zone.lock().free_pages(page_ptr);
     }
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        !page_ptr.flags().has(PageFlags::FREE) && page_ptr.flags().has(PageFlags::BUDDY)
+    fn has_management_over(&self, page_ptr: Self::RawPage) -> bool {
+        !page_ptr.is_free() && page_ptr.is_buddy()
     }
 }
 

+ 32 - 31
crates/buddy_allocator/src/zone.rs

@@ -1,78 +1,82 @@
-use crate::BuddyPFNOps as _;
-
 use super::free_area::FreeArea;
+use crate::{BuddyPFNOps as _, BuddyRawPage};
 use core::sync::atomic::Ordering;
 use eonix_mm::{
     address::{AddrOps as _, PAddr},
-    paging::{PageFlags, RawPagePtr, PFN},
+    paging::PFN,
 };
 
-pub(super) struct Zone<const AREAS: usize> {
-    free_areas: [FreeArea; AREAS],
+pub(super) struct Zone<T, const AREAS: usize> {
+    free_areas: [FreeArea<T>; AREAS],
 }
 
-impl<const AREAS: usize> Zone<AREAS> {
+impl<Raw, const AREAS: usize> Zone<Raw, AREAS>
+where
+    Raw: BuddyRawPage,
+{
     pub const fn new() -> Self {
         Self {
             free_areas: [const { FreeArea::new() }; AREAS],
         }
     }
 
-    pub fn get_free_pages(&mut self, order: u32) -> Option<RawPagePtr> {
+    pub fn get_free_pages(&mut self, order: u32) -> Option<Raw> {
         for current_order in order..AREAS as u32 {
             let pages_ptr = self.free_areas[current_order as usize].get_free_pages();
             let Some(pages_ptr) = pages_ptr else { continue };
 
-            pages_ptr.as_mut().order = order;
+            pages_ptr.set_order(order);
 
             if current_order > order {
                 self.expand(pages_ptr, current_order, order);
             }
-            assert!(pages_ptr.flags().has(PageFlags::PRESENT | PageFlags::FREE));
+            assert!(pages_ptr.is_free() && pages_ptr.is_present());
 
             return Some(pages_ptr);
         }
         None
     }
 
-    fn expand(&mut self, pages_ptr: RawPagePtr, order: u32, target_order: u32) {
+    fn expand(&mut self, pages_ptr: Raw, order: u32, target_order: u32) {
         let mut offset = 1 << order;
+        let pages_pfn = Into::<PFN>::into(pages_ptr);
 
         for order in (target_order..order).rev() {
             offset >>= 1;
-            let split_pages_ptr = pages_ptr.offset(offset);
-            split_pages_ptr.as_mut().order = order;
-            split_pages_ptr.flags().set(PageFlags::BUDDY);
+
+            let split_pages_ptr = Raw::from(pages_pfn + offset);
+            split_pages_ptr.set_order(order);
+            split_pages_ptr.set_buddy();
             self.free_areas[order as usize].add_pages(split_pages_ptr);
         }
     }
 
-    pub fn free_pages(&mut self, mut pages_ptr: RawPagePtr) {
+    pub fn free_pages(&mut self, mut pages_ptr: Raw) {
         assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
 
-        let mut pfn = PFN::from(pages_ptr);
+        let mut pfn = Into::<PFN>::into(pages_ptr);
         let mut current_order = pages_ptr.order();
 
         while current_order < (AREAS - 1) as u32 {
             let buddy_pfn = pfn.buddy_pfn(current_order);
-            let buddy_pages_ptr = RawPagePtr::from(buddy_pfn);
+            let buddy_pages_ptr = Raw::from(buddy_pfn);
 
             if !self.buddy_check(buddy_pages_ptr, current_order) {
                 break;
             }
 
-            pages_ptr.flags().clear(PageFlags::BUDDY);
-            buddy_pages_ptr.flags().clear(PageFlags::BUDDY);
+            pages_ptr.clear_buddy();
+            buddy_pages_ptr.clear_buddy();
             self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
 
-            pages_ptr = RawPagePtr::from(pfn.combined_pfn(buddy_pfn));
+            pages_ptr = Raw::from(pfn.combined_pfn(buddy_pfn));
             pfn = pfn.combined_pfn(buddy_pfn);
 
-            pages_ptr.flags().set(PageFlags::BUDDY);
+            pages_ptr.set_buddy();
             current_order += 1;
         }
 
-        pages_ptr.as_mut().order = current_order;
+        pages_ptr.set_order(current_order);
         self.free_areas[current_order as usize].add_pages(pages_ptr);
     }
 
@@ -81,18 +85,15 @@ impl<const AREAS: usize> Zone<AREAS> {
     /// - the buddy is valid(present) &&
     /// - the buddy is right now in free_areas &&
     /// - a page and its buddy have the same order &&
-    /// - a page and its buddy are in the same zone.    // check when smp
-    fn buddy_check(&self, pages_ptr: RawPagePtr, order: u32) -> bool {
-        if !pages_ptr.flags().has(PageFlags::PRESENT) {
-            return false;
-        }
-        if !pages_ptr.flags().has(PageFlags::FREE) {
+    /// - a page and its buddy are in the same zone (on smp systems).
+    fn buddy_check(&self, pages_ptr: Raw, order: u32) -> bool {
+        if !pages_ptr.is_present() {
             return false;
         }
-        if pages_ptr.flags().has(PageFlags::LOCAL) {
+        if !pages_ptr.is_free() {
             return false;
         }
-        if pages_ptr.as_ref().order != order {
+        if pages_ptr.order() != order {
             return false;
         }
 
@@ -113,8 +114,8 @@ impl<const AREAS: usize> Zone<AREAS> {
             while start_pfn + order as usize > end_pfn {
                 order -= 1;
             }
-            let page_ptr: RawPagePtr = start_pfn.into();
-            page_ptr.flags().set(PageFlags::BUDDY);
+            let page_ptr = Raw::from(start_pfn);
+            page_ptr.set_buddy();
             self.free_areas[order as usize].add_pages(page_ptr);
             start_pfn = start_pfn + (1 << order) as usize;
         }

+ 0 - 1
crates/eonix_mm/Cargo.toml

@@ -4,4 +4,3 @@ version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-intrusive_list = { path = "../intrusive_list" }

+ 22 - 6
crates/eonix_mm/src/page_table/page_table.rs

@@ -6,7 +6,7 @@ use super::{
 use crate::{
     address::{PAddr, VRange},
     page_table::PageTableIterator,
-    paging::{Page, PageAccess, PageAlloc, PageBlock},
+    paging::{GlobalPageAlloc, Page, PageAccess, PageAlloc, PageBlock},
 };
 use core::{marker::PhantomData, ptr::NonNull};
 
@@ -41,8 +41,8 @@ where
     A: PageAlloc,
     X: PageAccess,
 {
-    pub fn new<A1: PageAlloc>(kernel_root_table_page: &Page<A1>) -> Self {
-        let new_root_table_page = Page::<A>::alloc();
+    pub fn new_in<A1: PageAlloc>(kernel_root_table_page: &Page<A1>, alloc: A) -> Self {
+        let new_root_table_page = Page::alloc_in(alloc);
         let new_table_data = X::get_ptr_for_page(&new_root_table_page);
         let kernel_table_data = X::get_ptr_for_page(kernel_root_table_page);
 
@@ -75,28 +75,32 @@ where
     }
 
     pub fn iter_user(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
+        let alloc = self.root_table_page.allocator();
         let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
         let root_page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
         };
 
-        PageTableIterator::<M, A, X, UserIterator>::new(root_page_table, range)
+        PageTableIterator::<M, A, X, UserIterator>::new(root_page_table, range, alloc.clone())
     }
 
     pub fn iter_kernel(&self, range: VRange) -> impl Iterator<Item = &mut M::Entry> {
+        let alloc = self.root_table_page.allocator();
         let page_table_ptr = X::get_ptr_for_page(&self.root_table_page);
         let root_page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
             M::RawTable::from_ptr(page_table_ptr)
         };
 
-        PageTableIterator::<M, A, X, KernelIterator>::new(root_page_table, range)
+        PageTableIterator::<M, A, X, KernelIterator>::new(root_page_table, range, alloc.clone())
     }
 
     fn drop_page_table_recursive(page_table: &Page<A>, levels: &[PageTableLevel]) {
         let [level, remaining_levels @ ..] = levels else { return };
 
+        let alloc = page_table.allocator();
+
         let page_table_ptr = X::get_ptr_for_page(page_table);
         let mut page_table = unsafe {
             // SAFETY: `page_table_ptr` is a valid pointer to a page table.
@@ -111,7 +115,7 @@ where
 
             let page_table = unsafe {
                 // SAFETY: We got the pfn from a valid page table entry, so it should be valid.
-                Page::<A>::from_raw(pfn)
+                Page::from_raw_in(pfn, alloc.clone())
             };
 
             Self::drop_page_table_recursive(&page_table, remaining_levels);
@@ -119,6 +123,18 @@ where
     }
 }
 
+impl<'a, M, A, X> PageTable<'a, M, A, X>
+where
+    M: PagingMode,
+    M::Entry: 'a,
+    A: GlobalPageAlloc,
+    X: PageAccess,
+{
+    pub fn new<A1: PageAlloc>(kernel_root_table_page: &Page<A1>) -> Self {
+        Self::new_in(kernel_root_table_page, A::global())
+    }
+}
+
 impl<'a, M, A, X> Drop for PageTable<'a, M, A, X>
 where
     M: PagingMode,

+ 7 - 5
crates/eonix_mm/src/page_table/pte_iterator.rs

@@ -11,7 +11,7 @@ pub struct UserIterator;
 pub trait IteratorType<M: PagingMode> {
     fn page_table_attributes() -> <M::Entry as PTE>::Attr;
 
-    fn get_page_table<'a, A, X>(pte: &mut M::Entry) -> M::RawTable<'a>
+    fn get_page_table<'a, A, X>(pte: &mut M::Entry, alloc: &A) -> M::RawTable<'a>
     where
         A: PageAlloc,
         X: PageAccess,
@@ -28,7 +28,7 @@ pub trait IteratorType<M: PagingMode> {
                 M::RawTable::from_ptr(page_table_ptr)
             }
         } else {
-            let page = Page::<A>::alloc();
+            let page = Page::alloc_in(alloc.clone());
             let page_table_ptr = X::get_ptr_for_page(&page);
 
             unsafe {
@@ -59,7 +59,8 @@ where
     indicies: [u16; 8],
     tables: [Option<M::RawTable<'a>>; 8],
 
-    _phantom: PhantomData<&'a (A, X, K)>,
+    alloc: A,
+    _phantom: PhantomData<&'a (X, K)>,
 }
 
 impl<'a, M, A, X, K> PageTableIterator<'a, M, A, X, K>
@@ -88,11 +89,11 @@ where
             };
             let parent_table = parent_table.as_mut().expect("Parent table is None");
             let next_pte = parent_table.index_mut(pt_idx);
-            child_table.replace(K::get_page_table::<A, X>(next_pte));
+            child_table.replace(K::get_page_table::<A, X>(next_pte, &self.alloc));
         }
     }
 
-    pub fn new(page_table: M::RawTable<'a>, range: VRange) -> Self {
+    pub fn new(page_table: M::RawTable<'a>, range: VRange, alloc: A) -> Self {
         let start = range.start().floor();
         let end = range.end().ceil();
 
@@ -100,6 +101,7 @@ where
             remaining: (end - start) / PAGE_SIZE,
             indicies: [0; 8],
             tables: [const { None }; 8],
+            alloc,
             _phantom: PhantomData,
         };
 

+ 2 - 2
crates/eonix_mm/src/paging.rs

@@ -4,6 +4,6 @@ mod pfn;
 mod raw_page;
 
 pub use page::{Page, PageAccess, PageBlock, PAGE_SIZE, PAGE_SIZE_BITS};
-pub use page_alloc::PageAlloc;
+pub use page_alloc::{GlobalPageAlloc, PageAlloc};
 pub use pfn::PFN;
-pub use raw_page::{PageFlags, RawPage, RawPagePtr};
+pub use raw_page::RawPage;

+ 137 - 50
crates/eonix_mm/src/paging/page.rs

@@ -1,6 +1,6 @@
-use super::{raw_page::RawPagePtr, PageAlloc, PFN};
+use super::{GlobalPageAlloc, PageAlloc, RawPage as _, PFN};
 use crate::address::{AddrRange, PAddr};
-use core::{fmt, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
+use core::{fmt, mem::ManuallyDrop, ptr::NonNull, sync::atomic::Ordering};
 
 pub const PAGE_SIZE: usize = 4096;
 pub const PAGE_SIZE_BITS: u32 = PAGE_SIZE.trailing_zeros();
@@ -35,66 +35,126 @@ pub trait PageAccess {
 /// A Page allocated in allocator `A`.
 #[derive(PartialEq, Eq, PartialOrd, Ord)]
 pub struct Page<A: PageAlloc> {
-    raw_page: RawPagePtr,
-    _phantom: PhantomData<A>,
+    raw_page: A::RawPage,
+    alloc: A,
 }
 
 unsafe impl<A: PageAlloc> Send for Page<A> {}
 unsafe impl<A: PageAlloc> Sync for Page<A> {}
 
-impl<A: PageAlloc> Page<A> {
+impl<A> Page<A>
+where
+    A: GlobalPageAlloc,
+{
     /// Allocate a page of the given *order*.
     pub fn alloc_order(order: u32) -> Self {
-        Self {
-            raw_page: A::alloc_order(order).expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_order_in(order, A::global())
     }
 
     /// Allocate exactly one page.
     pub fn alloc() -> Self {
-        Self {
-            raw_page: A::alloc().expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_in(A::global())
     }
 
     /// Allocate a contiguous block of pages that can contain at least `count` pages.
     pub fn alloc_at_least(count: usize) -> Self {
-        Self {
-            raw_page: A::alloc_at_least(count).expect("Out of memory"),
-            _phantom: PhantomData,
-        }
+        Self::alloc_at_least_in(count, A::global())
     }
 
-    /// Whether we are the only owner of the page.
-    pub fn is_exclusive(&self) -> bool {
-        self.raw_page.refcount().load(Ordering::Acquire) == 1
+    /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
+    ///
+    /// # Safety
+    /// This function is unsafe because it assumes that the caller has ensured that
+    /// `pfn` points to a valid page allocated through `alloc_order()` and that the
+    /// page have not been freed or deallocated yet.
+    ///
+    /// No checks are done. Any violation of this assumption may lead to undefined behavior.
+    pub unsafe fn from_raw_unchecked(pfn: PFN) -> Self {
+        unsafe { Self::from_raw_unchecked_in(pfn, A::global()) }
     }
 
-    /// Returns the *order* of the page, which is the log2 of the number of pages
-    /// contained in the page object.
-    pub fn order(&self) -> u32 {
-        self.raw_page.order()
+    /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
+    ///
+    /// This function is a safe wrapper around `from_paddr_unchecked()` that does **some sort
+    /// of** checks to ensure that the page is valid and managed by the allocator.
+    ///
+    /// # Panic
+    /// This function will panic if the page is not valid or if the page is not managed by
+    /// the allocator.
+    ///
+    /// # Safety
+    /// This function is unsafe because it assumes that the caller has ensured that
+    /// `pfn` points to an existing page (A.K.A. inside the global page array) and the
+    /// page will not be freed or deallocated during the call.
+    pub unsafe fn from_raw(pfn: PFN) -> Self {
+        unsafe { Self::from_raw_in(pfn, A::global()) }
     }
 
-    /// Returns the total size of the page in bytes.
-    pub fn len(&self) -> usize {
-        1 << (self.order() + PAGE_SIZE_BITS)
+    /// Do some work with the page without touching the reference count with the same
+    /// restrictions as `from_raw_in()`.
+    ///
+    /// # Safety
+    /// Check `from_raw()` for the safety requirements.
+    pub unsafe fn with_raw<F, O>(pfn: PFN, func: F) -> O
+    where
+        F: FnOnce(&Self) -> O,
+    {
+        unsafe { Self::with_raw_in(pfn, func, A::global()) }
+    }
+
+    /// Do some work with the page without touching the reference count with the same
+    /// restrictions as `from_raw_unchecked_in()`.
+    ///
+    /// # Safety
+    /// Check `from_raw_unchecked()` for the safety requirements.
+    pub unsafe fn with_raw_unchecked<F, O>(pfn: PFN, func: F, alloc: A) -> O
+    where
+        F: FnOnce(&Self) -> O,
+    {
+        unsafe { Self::with_raw_unchecked_in(pfn, func, alloc) }
+    }
+}
+
+impl<A> Page<A>
+where
+    A: PageAlloc,
+{
+    /// Allocate a page of the given *order*.
+    pub fn alloc_order_in(order: u32, alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc_order(order).expect("Out of memory"),
+            alloc,
+        }
+    }
+
+    /// Allocate exactly one page.
+    pub fn alloc_in(alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc().expect("Out of memory"),
+            alloc,
+        }
+    }
+
+    /// Allocate a contiguous block of pages that can contain at least `count` pages.
+    pub fn alloc_at_least_in(count: usize, alloc: A) -> Self {
+        Self {
+            raw_page: alloc.alloc_at_least(count).expect("Out of memory"),
+            alloc,
+        }
     }
 
     /// Acquire the ownership of the page pointed to by `pfn`, leaving `refcount` untouched.
     ///
     /// # Safety
     /// This function is unsafe because it assumes that the caller has ensured that
-    /// `pfn` points to a valid page allocated through `alloc_order()` and that the
-    /// page have not been freed or deallocated yet.
+    /// `pfn` points to a valid page managed by `alloc` and that the page have not
+    /// been freed or deallocated yet.
     ///
     /// No checks are done. Any violation of this assumption may lead to undefined behavior.
-    pub unsafe fn from_raw_unchecked(pfn: PFN) -> Self {
+    pub unsafe fn from_raw_unchecked_in(pfn: PFN, alloc: A) -> Self {
         Self {
-            raw_page: RawPagePtr::from(pfn),
-            _phantom: PhantomData,
+            raw_page: A::RawPage::from(pfn),
+            alloc,
         }
     }
 
@@ -111,47 +171,63 @@ impl<A: PageAlloc> Page<A> {
     /// This function is unsafe because it assumes that the caller has ensured that
     /// `pfn` points to an existing page (A.K.A. inside the global page array) and the
     /// page will not be freed or deallocated during the call.
-    pub unsafe fn from_raw(pfn: PFN) -> Self {
+    pub unsafe fn from_raw_in(pfn: PFN, alloc: A) -> Self {
         unsafe {
             // SAFETY: The caller guarantees that the page is inside the global page array.
-            assert!(A::has_management_over(RawPagePtr::from(pfn)));
+            assert!(alloc.has_management_over(A::RawPage::from(pfn)));
 
             // SAFETY: We've checked that the validity of the page. And the caller guarantees
             //         that the page will not be freed or deallocated during the call.
-            Self::from_raw_unchecked(pfn)
+            Self::from_raw_unchecked_in(pfn, alloc)
         }
     }
 
     /// Do some work with the page without touching the reference count with the same
-    /// restrictions as `from_raw()`.
+    /// restrictions as `from_raw_in()`.
     ///
     /// # Safety
-    /// Check `from_raw()` for the safety requirements.
-    pub unsafe fn with_raw<F, O>(pfn: PFN, func: F) -> O
+    /// Check `from_raw_in()` for the safety requirements.
+    pub unsafe fn with_raw_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
     where
         F: FnOnce(&Self) -> O,
     {
         unsafe {
-            let me = ManuallyDrop::new(Self::from_raw(pfn));
+            let me = ManuallyDrop::new(Self::from_raw_in(pfn, alloc));
             func(&me)
         }
     }
 
     /// Do some work with the page without touching the reference count with the same
-    /// restrictions as `from_raw_unchecked()`.
+    /// restrictions as `from_raw_unchecked_in()`.
     ///
     /// # Safety
-    /// Check `from_raw_unchecked()` for the safety requirements.
-    pub unsafe fn with_raw_unchecked<F, O>(pfn: PFN, func: F) -> O
+    /// Check `from_raw_unchecked_in()` for the safety requirements.
+    pub unsafe fn with_raw_unchecked_in<F, O>(pfn: PFN, func: F, alloc: A) -> O
     where
         F: FnOnce(&Self) -> O,
     {
         unsafe {
-            let me = ManuallyDrop::new(Self::from_raw_unchecked(pfn));
+            let me = ManuallyDrop::new(Self::from_raw_unchecked_in(pfn, alloc));
             func(&me)
         }
     }
 
+    /// Whether we are the only owner of the page.
+    pub fn is_exclusive(&self) -> bool {
+        self.raw_page.refcount().load(Ordering::Acquire) == 1
+    }
+
+    /// Returns the *order* of the page, which is the log2 of the number of pages
+    /// contained in the page object.
+    pub fn order(&self) -> u32 {
+        self.raw_page.order()
+    }
+
+    /// Returns the total size of the page in bytes.
+    pub fn len(&self) -> usize {
+        1 << (self.order() + PAGE_SIZE_BITS)
+    }
+
     /// Consumes the `Page` and returns the physical frame number without dropping
     /// the reference count the page holds.
     pub fn into_raw(self) -> PFN {
@@ -162,7 +238,7 @@ impl<A: PageAlloc> Page<A> {
     /// Returns the physical frame number of the page, which is aligned with the
     /// page size and valid.
     pub fn pfn(&self) -> PFN {
-        PFN::from(self.raw_page)
+        Into::<PFN>::into(self.raw_page)
     }
 
     /// Returns the start physical address of the page, which is guaranteed to be
@@ -176,9 +252,17 @@ impl<A: PageAlloc> Page<A> {
     pub fn range(&self) -> AddrRange<PAddr> {
         AddrRange::from(self.start()).grow(self.len())
     }
+
+    /// Get the allocator that manages this page.
+    pub fn allocator(&self) -> &A {
+        &self.alloc
+    }
 }
 
-impl<A: PageAlloc> Clone for Page<A> {
+impl<A> Clone for Page<A>
+where
+    A: PageAlloc,
+{
     fn clone(&self) -> Self {
         // SAFETY: Memory order here can be Relaxed is for the same reason as that
         // in the copy constructor of `std::shared_ptr`.
@@ -186,21 +270,24 @@ impl<A: PageAlloc> Clone for Page<A> {
 
         Self {
             raw_page: self.raw_page,
-            _phantom: PhantomData,
+            alloc: self.alloc.clone(),
         }
     }
 }
 
-impl<A: PageAlloc> Drop for Page<A> {
+impl<A> Drop for Page<A>
+where
+    A: PageAlloc,
+{
     fn drop(&mut self) {
         match self.raw_page.refcount().fetch_sub(1, Ordering::AcqRel) {
             0 => panic!("Refcount for an in-use page is 0"),
             1 => unsafe {
                 // SAFETY: `self.raw_page` points to a valid page inside the global page array.
-                assert!(A::has_management_over(self.raw_page));
+                assert!(self.alloc.has_management_over(self.raw_page));
 
                 // SAFETY: `self.raw_page` is managed by the allocator and we're dropping the page.
-                A::dealloc(self.raw_page)
+                self.alloc.dealloc(self.raw_page)
             },
             _ => {}
         }
@@ -212,7 +299,7 @@ impl<A: PageAlloc> fmt::Debug for Page<A> {
         write!(
             f,
             "Page({:?}, order={})",
-            usize::from(PFN::from(self.raw_page)),
+            Into::<PFN>::into(self.raw_page),
             self.order()
         )
     }

+ 46 - 15
crates/eonix_mm/src/paging/page_alloc.rs

@@ -1,31 +1,62 @@
-use super::raw_page::RawPagePtr;
+use super::RawPage;
+
+/// A trait for allocating and deallocating pages of memory.
+///
+/// Note that the instances of this trait should provide pointer-like or reference-like
+/// behavior, meaning that the allocators are to be passed around by value and stored in
+/// managed data structures. This is because the allocator may be used to deallocate the
+/// pages it allocates.
+pub trait PageAlloc: Clone {
+    type RawPage: RawPage;
 
-pub trait PageAlloc: Sized {
     /// Allocate a page of the given *order*.
-    fn alloc_order(order: u32) -> Option<RawPagePtr>;
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage>;
 
     /// Allocate exactly one page.
-    fn alloc() -> Option<RawPagePtr> {
-        Self::alloc_order(0)
+    fn alloc(&self) -> Option<Self::RawPage> {
+        self.alloc_order(0)
     }
 
     /// Allocate a contiguous block of pages that can contain at least `count` pages.
-    fn alloc_at_least(count: usize) -> Option<RawPagePtr> {
+    fn alloc_at_least(&self, count: usize) -> Option<Self::RawPage> {
         let order = count.next_power_of_two().trailing_zeros();
-        Self::alloc_order(order)
+        self.alloc_order(order)
     }
 
     /// Deallocate a page.
     ///
     /// # Safety
-    /// This function is unsafe because it assumes that the caller has ensured that
-    /// `page` is allocated in this allocator and never used after this call.
-    unsafe fn dealloc(page_ptr: RawPagePtr);
+    /// This function is unsafe because it assumes that the caller MUST ensure that
+    /// `raw_page` is allocated in this allocator and never used after this call.
+    unsafe fn dealloc(&self, raw_page: Self::RawPage);
 
     /// Check whether the page is allocated and managed by the allocator.
-    ///
-    /// # Safety
-    /// This function is unsafe because it assumes that the caller has ensured that
-    /// `page_ptr` points to a raw page inside the global page array.
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool;
+    fn has_management_over(&self, page_ptr: Self::RawPage) -> bool;
+}
+
+/// A trait for global page allocators.
+///
+/// Global means that we can get an instance of the allocator from anywhere in the kernel.
+pub trait GlobalPageAlloc: PageAlloc + 'static {
+    /// Get the global page allocator.
+    fn global() -> Self;
+}
+
+impl<'a, A> PageAlloc for &'a A
+where
+    A: PageAlloc,
+{
+    type RawPage = A::RawPage;
+
+    fn alloc_order(&self, order: u32) -> Option<Self::RawPage> {
+        (*self).alloc_order(order)
+    }
+
+    unsafe fn dealloc(&self, raw_page: Self::RawPage) {
+        unsafe { (*self).dealloc(raw_page) }
+    }
+
+    fn has_management_over(&self, raw_page: Self::RawPage) -> bool {
+        (*self).has_management_over(raw_page)
+    }
 }

+ 7 - 93
crates/eonix_mm/src/paging/raw_page.rs

@@ -1,97 +1,11 @@
 use super::PFN;
-use core::{
-    ptr::NonNull,
-    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
-};
-use intrusive_list::Link;
+use core::sync::atomic::AtomicUsize;
 
-const PAGE_ARRAY: NonNull<RawPage> =
-    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
+/// A `RawPage` represents a page of memory in the kernel. It is a low-level
+/// representation of a page that is used by the kernel to manage memory.
+pub trait RawPage: Clone + Copy + From<PFN> + Into<PFN> {
+    fn order(&self) -> u32;
+    fn refcount(&self) -> &AtomicUsize;
 
-pub struct PageFlags(AtomicU32);
-
-pub struct RawPage {
-    /// This can be used for LRU page swap in the future.
-    ///
-    /// Now only used for free page links in the buddy system.
-    pub link: Link,
-    /// # Safety
-    /// This field is only used in buddy system and is protected by the global lock.
-    pub order: u32,
-    pub flags: PageFlags,
-    pub refcount: AtomicUsize,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct RawPagePtr(NonNull<RawPage>);
-
-impl PageFlags {
-    pub const PRESENT: u32 = 1 << 0;
-    // pub const LOCKED: u32 = 1 << 1;
-    pub const BUDDY: u32 = 1 << 2;
-    // pub const SLAB: u32 = 1 << 3;
-    // pub const DIRTY: u32 = 1 << 4;
-    pub const FREE: u32 = 1 << 5;
-    pub const LOCAL: u32 = 1 << 6;
-
-    pub fn has(&self, flag: u32) -> bool {
-        (self.0.load(Ordering::Relaxed) & flag) == flag
-    }
-
-    pub fn set(&self, flag: u32) {
-        self.0.fetch_or(flag, Ordering::Relaxed);
-    }
-
-    pub fn clear(&self, flag: u32) {
-        self.0.fetch_and(!flag, Ordering::Relaxed);
-    }
-}
-
-impl RawPagePtr {
-    pub const fn new(ptr: NonNull<RawPage>) -> Self {
-        Self(ptr)
-    }
-
-    pub const fn as_ptr(self) -> *mut RawPage {
-        self.0.as_ptr()
-    }
-
-    pub const fn as_ref<'a>(self) -> &'a RawPage {
-        unsafe { &*self.as_ptr() }
-    }
-
-    pub const fn as_mut<'a>(self) -> &'a mut RawPage {
-        unsafe { &mut *self.as_ptr() }
-    }
-
-    pub const fn order(&self) -> u32 {
-        self.as_ref().order
-    }
-
-    pub const fn flags(&self) -> &PageFlags {
-        &self.as_ref().flags
-    }
-
-    pub const fn refcount(&self) -> &AtomicUsize {
-        &self.as_ref().refcount
-    }
-
-    pub const fn offset(&self, count: usize) -> Self {
-        let new_raw_ptr = unsafe { self.0.add(count) };
-        Self::new(new_raw_ptr)
-    }
-}
-
-impl From<RawPagePtr> for PFN {
-    fn from(value: RawPagePtr) -> Self {
-        let idx = unsafe { value.as_ptr().offset_from(PAGE_ARRAY.as_ptr()) as usize };
-        Self::from(idx)
-    }
-}
-
-impl From<PFN> for RawPagePtr {
-    fn from(pfn: PFN) -> Self {
-        let raw_page_ptr = unsafe { PAGE_ARRAY.add(usize::from(pfn)) };
-        Self::new(raw_page_ptr)
-    }
+    fn is_present(&self) -> bool;
 }

+ 36 - 0
crates/intrusive_list/src/lib.rs

@@ -2,11 +2,47 @@
 
 use core::ptr::NonNull;
 
+pub struct List {
+    head: Link,
+    count: usize,
+}
+
 pub struct Link {
     prev: Option<NonNull<Link>>,
     next: Option<NonNull<Link>>,
 }
 
+impl List {
+    pub const fn new() -> Self {
+        Self {
+            head: Link::new(),
+            count: 0,
+        }
+    }
+
+    pub const fn count(&self) -> usize {
+        self.count
+    }
+
+    pub fn insert(&mut self, node: &mut Link) {
+        self.head.insert(node);
+        self.count += 1;
+    }
+
+    pub fn remove(&mut self, node: &mut Link) {
+        node.remove();
+        self.count -= 1;
+    }
+
+    pub fn pop(&mut self) -> Option<&mut Link> {
+        self.head.next_mut().map(|node| {
+            self.count -= 1;
+            node.remove();
+            node
+        })
+    }
+}
+
 impl Link {
     pub const fn new() -> Self {
         Self {

+ 0 - 6
src/fs/procfs.rs

@@ -20,12 +20,6 @@ use eonix_runtime::task::Task;
 use eonix_sync::{AsProof as _, AsProofMut as _, LazyLock, Locked};
 use itertools::Itertools;
 
-fn split_len_offset(data: &[u8], len: usize, offset: usize) -> Option<&[u8]> {
-    let real_data = data.split_at_checked(len).map(|(data, _)| data)?;
-
-    real_data.split_at_checked(offset).map(|(_, data)| data)
-}
-
 #[allow(dead_code)]
 pub trait ProcFsFile: Send + Sync {
     fn can_read(&self) -> bool {

+ 0 - 61
src/intrusive_list.rs

@@ -1,61 +0,0 @@
-use core::ptr::NonNull;
-
-pub struct Link {
-    prev: Option<NonNull<Link>>,
-    next: Option<NonNull<Link>>,
-}
-
-#[allow(dead_code)]
-impl Link {
-    pub const fn new() -> Self {
-        Self {
-            prev: None,
-            next: None,
-        }
-    }
-
-    pub fn insert(&mut self, node: &mut Self) {
-        unsafe {
-            let insert_node = NonNull::new(node as *mut Self);
-            if let Some(next) = self.next {
-                (*next.as_ptr()).prev = insert_node;
-            }
-            node.next = self.next;
-            node.prev = NonNull::new(self as *mut Self);
-            self.next = insert_node;
-        }
-    }
-
-    pub fn remove(&mut self) {
-        if let Some(next) = self.next {
-            unsafe { (*next.as_ptr()).prev = self.prev };
-        }
-
-        if let Some(prev) = self.prev {
-            unsafe { (*prev.as_ptr()).next = self.next };
-        }
-
-        self.prev = None;
-        self.next = None;
-    }
-
-    pub fn next(&self) -> Option<&Self> {
-        self.next.map(|node| unsafe { &*node.as_ptr() })
-    }
-
-    pub fn next_mut(&mut self) -> Option<&mut Self> {
-        self.next.map(|node| unsafe { &mut *node.as_ptr() })
-    }
-}
-
-#[macro_export]
-macro_rules! container_of {
-    ($ptr:expr, $type:ty, $($f:tt)*) => {{
-        let ptr = $ptr as *const _ as *const u8;
-        let offset: usize = ::core::mem::offset_of!($type, $($f)*);
-        ptr.sub(offset) as *mut $type
-    }}
-}
-
-#[allow(unused_imports)]
-pub use container_of;

+ 79 - 65
src/kernel/mem/page_alloc.rs

@@ -1,26 +1,35 @@
+mod raw_page;
+
 use super::{paging::AllocZeroed as _, Page};
-use buddy_allocator::{BuddyAllocator, FreeArea as BuddyFreeArea};
+use buddy_allocator::{BuddyAllocator, BuddyRawPage as _};
 use core::{ptr::NonNull, sync::atomic::Ordering};
 use eonix_mm::{
     address::{AddrOps as _, PAddr},
-    paging::{PageAlloc, PageFlags, RawPagePtr, PFN},
+    paging::{GlobalPageAlloc as GlobalPageAllocTrait, PageAlloc, PFN},
 };
+use intrusive_list::List;
+use raw_page::{PageFlags, RawPage, RawPagePtr};
 
 const COSTLY_ORDER: u32 = 3;
 const BATCH_SIZE: u32 = 64;
 
+static REAL_BUDDY_ALLOC: BuddyAllocator<RawPagePtr> = BuddyAllocator::new();
+static BUDDY_ALLOC: &'static BuddyAllocator<RawPagePtr> = &REAL_BUDDY_ALLOC;
+
 #[arch::define_percpu]
 static PERCPU_PAGE_ALLOC: PerCpuPageAlloc = PerCpuPageAlloc::new();
 
+#[derive(Clone)]
 pub struct NoAlloc;
 
+#[derive(Clone)]
 pub struct GlobalPageAlloc;
 
 struct PerCpuPageAlloc {
     batch: u32,
     // TODO: might be used in the future.
     // high: u32,
-    free_areas: [BuddyFreeArea; COSTLY_ORDER as usize + 1],
+    free_areas: [List; COSTLY_ORDER as usize + 1],
 }
 
 impl PerCpuPageAlloc {
@@ -28,107 +37,111 @@ impl PerCpuPageAlloc {
         Self {
             batch: BATCH_SIZE,
             // high: 0,
-            free_areas: [const { BuddyFreeArea::new() }; COSTLY_ORDER as usize + 1],
+            free_areas: [const { List::new() }; COSTLY_ORDER as usize + 1],
         }
     }
 
-    fn do_alloc_order(&mut self, order: u32) -> Option<RawPagePtr> {
-        assert!(order <= COSTLY_ORDER);
+    fn insert_free_pages(&mut self, pages_ptr: RawPagePtr, order: u32) {
         let free_area = &mut self.free_areas[order as usize];
+        free_area.insert(unsafe { pages_ptr.get_link() });
+    }
 
-        let mut page_ptr = free_area.get_free_pages();
-
-        if page_ptr.is_none() {
-            let batch = self.batch >> order;
-            for _ in 0..batch {
-                if let Some(pages_ptr) = BuddyAllocator::alloc_order(order) {
-                    pages_ptr.flags().set(PageFlags::LOCAL);
-                    free_area.add_pages(pages_ptr);
-                } else {
-                    break;
-                };
-            }
+    fn get_free_pages(&mut self, order: u32) -> Option<RawPagePtr> {
+        let free_area = &mut self.free_areas[order as usize];
+        free_area.pop().map(|node| unsafe {
+            // SAFETY: `node` is a valid pointer to a `Link` that is not used by anyone.
+            RawPagePtr::from_link(node)
+        })
+    }
+
+    fn alloc_order(&mut self, order: u32) -> Option<RawPagePtr> {
+        assert!(order <= COSTLY_ORDER);
+        if let Some(pages) = self.get_free_pages(order) {
+            return Some(pages);
+        }
 
-            page_ptr = free_area.get_free_pages();
+        let batch = self.batch >> order;
+        for _ in 0..batch {
+            if let Some(pages_ptr) = BUDDY_ALLOC.alloc_order(order) {
+                pages_ptr.flags().set(PageFlags::LOCAL);
+                self.insert_free_pages(pages_ptr, order);
+            } else {
+                break;
+            };
         }
 
-        page_ptr.inspect(|page_ptr| page_ptr.flags().clear(PageFlags::FREE))
+        self.get_free_pages(order)
     }
 
     fn free_pages(&mut self, pages_ptr: RawPagePtr, order: u32) {
         assert_eq!(pages_ptr.order(), order);
         assert_eq!(pages_ptr.refcount().load(Ordering::Relaxed), 0);
 
-        // TODO: Temporary workaround here.
         pages_ptr.refcount().store(1, Ordering::Relaxed);
-        self.free_areas[order as usize].add_pages(pages_ptr);
+        self.insert_free_pages(pages_ptr, order);
     }
 }
 
 impl PageAlloc for GlobalPageAlloc {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
+    type RawPage = RawPagePtr;
+
+    fn alloc_order(&self, order: u32) -> Option<RawPagePtr> {
         if order > COSTLY_ORDER {
-            BuddyAllocator::alloc_order(order)
+            BUDDY_ALLOC.alloc_order(order)
         } else {
-            PerCpuPageAlloc::alloc_order(order)
+            unsafe {
+                eonix_preempt::disable();
+                let page_ptr = PERCPU_PAGE_ALLOC.as_mut().alloc_order(order);
+                eonix_preempt::enable();
+                page_ptr
+            }
         }
     }
 
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
+    unsafe fn dealloc(&self, page_ptr: RawPagePtr) {
         if page_ptr.order() > COSTLY_ORDER {
-            BuddyAllocator::dealloc(page_ptr);
+            BUDDY_ALLOC.dealloc(page_ptr);
         } else {
-            PerCpuPageAlloc::dealloc(page_ptr);
+            let order = page_ptr.order();
+            unsafe {
+                eonix_preempt::disable();
+                PERCPU_PAGE_ALLOC.as_mut().free_pages(page_ptr, order);
+                eonix_preempt::enable();
+            }
         }
     }
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        if page_ptr.order() > COSTLY_ORDER {
-            BuddyAllocator::has_management_over(page_ptr)
-        } else {
-            PerCpuPageAlloc::has_management_over(page_ptr)
-        }
+    fn has_management_over(&self, page_ptr: RawPagePtr) -> bool {
+        BUDDY_ALLOC.has_management_over(page_ptr)
+            && (page_ptr.order() > COSTLY_ORDER || page_ptr.flags().has(PageFlags::LOCAL))
     }
 }
 
 impl PageAlloc for NoAlloc {
-    fn alloc_order(_order: u32) -> Option<RawPagePtr> {
+    type RawPage = RawPagePtr;
+
+    fn alloc_order(&self, _order: u32) -> Option<RawPagePtr> {
         panic!("NoAlloc cannot allocate pages");
     }
 
-    unsafe fn dealloc(_: RawPagePtr) {
+    unsafe fn dealloc(&self, _: RawPagePtr) {
         panic!("NoAlloc cannot deallocate pages");
     }
 
-    unsafe fn has_management_over(_: RawPagePtr) -> bool {
+    fn has_management_over(&self, _: RawPagePtr) -> bool {
         true
     }
 }
 
-impl PageAlloc for PerCpuPageAlloc {
-    fn alloc_order(order: u32) -> Option<RawPagePtr> {
-        let page_ptr;
-        unsafe {
-            eonix_preempt::disable();
-            page_ptr = PERCPU_PAGE_ALLOC.as_mut().do_alloc_order(order);
-            eonix_preempt::enable();
-        }
-
-        page_ptr
-    }
-
-    unsafe fn dealloc(page_ptr: RawPagePtr) {
-        let order = page_ptr.order();
-
-        unsafe {
-            eonix_preempt::disable();
-            PERCPU_PAGE_ALLOC.as_mut().free_pages(page_ptr, order);
-            eonix_preempt::enable();
-        }
+impl GlobalPageAllocTrait for GlobalPageAlloc {
+    fn global() -> Self {
+        GlobalPageAlloc
     }
+}
 
-    unsafe fn has_management_over(page_ptr: RawPagePtr) -> bool {
-        BuddyAllocator::has_management_over(page_ptr) && page_ptr.flags().has(PageFlags::LOCAL)
+impl GlobalPageAllocTrait for NoAlloc {
+    fn global() -> Self {
+        NoAlloc
     }
 }
 
@@ -145,7 +158,7 @@ pub extern "C" fn mark_present(start: usize, end: usize) {
 
 #[no_mangle]
 pub extern "C" fn create_pages(start: PAddr, end: PAddr) {
-    BuddyAllocator::create_pages(start, end);
+    REAL_BUDDY_ALLOC.create_pages(start, end);
 }
 
 #[no_mangle]
@@ -155,15 +168,16 @@ pub extern "C" fn page_to_pfn(page: *const ()) -> PFN {
 }
 
 #[no_mangle]
-pub extern "C" fn c_alloc_page() -> *const () {
-    GlobalPageAlloc::alloc().expect("Out of memory").as_ptr() as *const _
+pub extern "C" fn c_alloc_page() -> *const RawPage {
+    GlobalPageAlloc.alloc().expect("Out of memory").as_ref()
 }
 
 #[no_mangle]
-pub extern "C" fn c_alloc_pages(order: u32) -> *const () {
-    GlobalPageAlloc::alloc_order(order)
+pub extern "C" fn c_alloc_pages(order: u32) -> *const RawPage {
+    GlobalPageAlloc
+        .alloc_order(order)
         .expect("Out of memory")
-        .as_ptr() as *const _
+        .as_ref()
 }
 
 #[no_mangle]

+ 150 - 0
src/kernel/mem/page_alloc/raw_page.rs

@@ -0,0 +1,150 @@
+use buddy_allocator::BuddyRawPage;
+use core::{
+    ptr::NonNull,
+    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
+};
+use eonix_mm::paging::{RawPage as RawPageTrait, PFN};
+use intrusive_list::{container_of, Link};
+
+const PAGE_ARRAY: NonNull<RawPage> =
+    unsafe { NonNull::new_unchecked(0xffffff8040000000 as *mut _) };
+
+pub struct PageFlags(AtomicU32);
+
+pub struct RawPage {
+    /// This can be used for LRU page swap in the future.
+    ///
+    /// Now only used for free page links in the buddy system.
+    pub link: Link,
+    /// # Safety
+    /// This field is only used in buddy system and is protected by the global lock.
+    pub order: u32,
+    pub flags: PageFlags,
+    pub refcount: AtomicUsize,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct RawPagePtr(NonNull<RawPage>);
+
+impl PageFlags {
+    pub const PRESENT: u32 = 1 << 0;
+    // pub const LOCKED: u32 = 1 << 1;
+    pub const BUDDY: u32 = 1 << 2;
+    // pub const SLAB: u32 = 1 << 3;
+    // pub const DIRTY: u32 = 1 << 4;
+    pub const FREE: u32 = 1 << 5;
+    pub const LOCAL: u32 = 1 << 6;
+
+    pub fn has(&self, flag: u32) -> bool {
+        (self.0.load(Ordering::Relaxed) & flag) == flag
+    }
+
+    pub fn set(&self, flag: u32) {
+        self.0.fetch_or(flag, Ordering::Relaxed);
+    }
+
+    pub fn clear(&self, flag: u32) {
+        self.0.fetch_and(!flag, Ordering::Relaxed);
+    }
+}
+
+impl RawPagePtr {
+    pub const fn new(ptr: NonNull<RawPage>) -> Self {
+        Self(ptr)
+    }
+
+    /// Get a raw pointer to the underlying `RawPage` struct.
+    ///
+    /// # Safety
+    /// Doing arithmetic on the pointer returned will cause immediate undefined behavior.
+    pub const unsafe fn as_ptr(self) -> *mut RawPage {
+        self.0.as_ptr()
+    }
+
+    pub const fn as_ref<'a>(self) -> &'a RawPage {
+        unsafe { &*self.as_ptr() }
+    }
+
+    pub const fn as_mut<'a>(self) -> &'a mut RawPage {
+        unsafe { &mut *self.as_ptr() }
+    }
+
+    pub const fn order(&self) -> u32 {
+        self.as_ref().order
+    }
+
+    pub const fn flags(&self) -> &PageFlags {
+        &self.as_ref().flags
+    }
+
+    pub const fn refcount(&self) -> &AtomicUsize {
+        &self.as_ref().refcount
+    }
+}
+
+impl From<RawPagePtr> for PFN {
+    fn from(value: RawPagePtr) -> Self {
+        let idx = unsafe { value.as_ptr().offset_from(PAGE_ARRAY.as_ptr()) as usize };
+        Self::from(idx)
+    }
+}
+
+impl From<PFN> for RawPagePtr {
+    fn from(pfn: PFN) -> Self {
+        let raw_page_ptr = unsafe { PAGE_ARRAY.add(usize::from(pfn)) };
+        Self::new(raw_page_ptr)
+    }
+}
+
+impl RawPageTrait for RawPagePtr {
+    fn order(&self) -> u32 {
+        self.order()
+    }
+
+    fn refcount(&self) -> &AtomicUsize {
+        self.refcount()
+    }
+
+    fn is_present(&self) -> bool {
+        self.flags().has(PageFlags::PRESENT)
+    }
+}
+
+impl BuddyRawPage for RawPagePtr {
+    unsafe fn from_link(link: &mut Link) -> Self {
+        let raw_page_ptr = container_of!(link, RawPage, link);
+        Self(raw_page_ptr)
+    }
+
+    fn set_order(&self, order: u32) {
+        self.as_mut().order = order;
+    }
+
+    unsafe fn get_link(&self) -> &mut Link {
+        &mut self.as_mut().link
+    }
+
+    fn is_buddy(&self) -> bool {
+        self.flags().has(PageFlags::BUDDY)
+    }
+
+    fn is_free(&self) -> bool {
+        self.flags().has(PageFlags::FREE)
+    }
+
+    fn set_buddy(&self) {
+        self.flags().set(PageFlags::BUDDY);
+    }
+
+    fn set_free(&self) {
+        self.flags().set(PageFlags::FREE);
+    }
+
+    fn clear_buddy(&self) {
+        self.flags().clear(PageFlags::BUDDY);
+    }
+
+    fn clear_free(&self) {
+        self.flags().clear(PageFlags::FREE);
+    }
+}

+ 0 - 1
src/lib.rs

@@ -16,7 +16,6 @@ mod driver;
 mod elf;
 mod fs;
 mod hash;
-mod intrusive_list;
 mod io;
 mod kernel;
 mod net;