소스 검색

Merge branch 'fix'

Fixed the problem that there might be two processes handling page fault
on the same page that is set copy on write. If they see that the
`refcount` is greater than `1`, they might each clone the page and
decrease the `refcount` of the old page. That will leave the old page
unfreed with a `refcount == 0`, which will cause bug in buddy allocator
when it tries to merge adjacent free pages.
greatbridf 3 주 전
부모
커밋
4602c4d71c
2개의 변경된 파일11개의 추가작업 그리고 5개의 파일을 삭제
  1. 6 5
      src/kernel/mem/mm_list/page_fault.rs
  2. 5 0
      src/kernel/mem/paging.rs

+ 6 - 5
src/kernel/mem/mm_list/page_fault.rs

@@ -1,5 +1,4 @@
 use arch::InterruptContext;
-use bindings::kernel::mem::paging::pfn_to_page;
 use bindings::{PA_A, PA_ANON, PA_COW, PA_MMAP, PA_P, PA_RW};
 use bitflags::bitflags;
 
@@ -86,10 +85,13 @@ impl MMList {
                 attributes &= !PA_RW as usize;
             }
 
-            // TODO!!!: Change this.
-            let page = unsafe { pfn_to_page(pfn).as_mut().unwrap() };
-            if page.refcount == 1 {
+            let page = unsafe { Page::take_pfn(pfn, 0) };
+            if unsafe { page.load_refcount() } == 1 {
+                // SAFETY: This is actually safe. If we read `1` here and we have `MMList` lock
+                // held, there couldn't be neither other processes sharing the page, nor other
+                // threads making the page COW at the same time.
                 pte.set_attributes(attributes);
+                core::mem::forget(page);
                 return Ok(());
             }
 
@@ -104,7 +106,6 @@ impl MMList {
             }
 
             attributes &= !(PA_A | PA_ANON) as usize;
-            page.refcount -= 1;
 
             pfn = new_page.into_pfn();
             pte.set(pfn, attributes);

+ 5 - 0
src/kernel/mem/paging.rs

@@ -7,6 +7,7 @@ use crate::bindings::root::EFAULT;
 use crate::io::{Buffer, FillResult};
 use crate::kernel::mem::phys;
 use core::fmt;
+use core::sync::atomic::{AtomicU64, Ordering};
 
 use super::phys::PhysPtr;
 
@@ -158,6 +159,10 @@ impl Page {
             c_increase_refcount(page);
         }
     }
+
+    pub unsafe fn load_refcount(&self) -> usize {
+        AtomicU64::from_ptr(&mut (*self.page_ptr).refcount).load(Ordering::Acquire) as usize
+    }
 }
 
 impl Clone for Page {