Bläddra i källkod

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 veckor sedan
förälder
incheckning
4602c4d71c
2 ändrade filer med 11 tillägg och 5 borttagningar
  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 {