diff --git a/crate/memory/src/page_table/mock_page_table.rs b/crate/memory/src/page_table/mock_page_table.rs index bf5f50d..c4aaea0 100644 --- a/crate/memory/src/page_table/mock_page_table.rs +++ b/crate/memory/src/page_table/mock_page_table.rs @@ -5,7 +5,7 @@ pub struct MockPageTable { mapped_set: BTreeSet, accessed_set: BTreeSet, dirty_set: BTreeSet, - page_fault_handler: PageFaultHandler, + page_fault_handler: Option, capacity: usize, } @@ -18,6 +18,12 @@ impl PageTable for MockPageTable { fn dirty(&self, addr: VirtAddr) -> bool { self.dirty_set.contains(&addr) } + fn clear_accessed(&mut self, addr: usize) { + self.accessed_set.remove(&addr); + } + fn clear_dirty(&mut self, addr: usize) { + self.dirty_set.remove(&addr); + } /// Map a page, return false if no more space fn map(&mut self, addr: VirtAddr) -> bool { if self.mapped_set.len() == self.capacity { @@ -32,30 +38,33 @@ impl PageTable for MockPageTable { } impl MockPageTable { - pub fn new(capacity: usize, page_fault_handler: PageFaultHandler) -> Self { + pub fn new(capacity: usize) -> Self { MockPageTable { mapped_set: BTreeSet::::new(), accessed_set: BTreeSet::::new(), dirty_set: BTreeSet::::new(), - page_fault_handler, + page_fault_handler: None, capacity, } } - /// Read memory, mark accessed, trigger page fault if not present - pub fn read(&mut self, addr: VirtAddr) { + pub fn set_handler(&mut self, page_fault_handler: PageFaultHandler) { + self.page_fault_handler = Some(page_fault_handler); + } + fn trigger_page_fault_if_not_present(&mut self, addr: VirtAddr) { while !self.mapped_set.contains(&addr) { let self_mut = unsafe{ &mut *(self as *mut Self) }; - (self.page_fault_handler)(self_mut, addr); + (self.page_fault_handler.as_mut().unwrap())(self_mut, addr); } + } + /// Read memory, mark accessed, trigger page fault if not present + pub fn read(&mut self, addr: VirtAddr) { + self.trigger_page_fault_if_not_present(addr); self.accessed_set.insert(addr); } /// Write memory, mark accessed and dirty, trigger page fault if not present pub fn write(&mut self, addr: VirtAddr) { - while !self.mapped_set.contains(&addr) { - let self_mut = unsafe{ &mut *(self as *mut Self) }; - (self.page_fault_handler)(self_mut, addr); - } + self.trigger_page_fault_if_not_present(addr); self.accessed_set.insert(addr); self.dirty_set.insert(addr); } @@ -71,7 +80,8 @@ mod test { fn test() { let page_fault_count = Arc::new(RefCell::new(0usize)); - let mut pt = MockPageTable::new(2, Box::new({ + let mut pt = MockPageTable::new(2); + pt.set_handler(Box::new({ let page_fault_count1 = page_fault_count.clone(); move |pt: &mut MockPageTable, addr: VirtAddr| { *page_fault_count1.borrow_mut() += 1; @@ -85,11 +95,17 @@ mod test { assert!(pt.accessed(0)); assert!(!pt.dirty(0)); + pt.clear_accessed(0); + assert!(!pt.accessed(0)); + pt.write(1); assert_eq!(*page_fault_count.borrow(), 1); assert!(pt.accessed(1)); assert!(pt.dirty(1)); + pt.clear_dirty(1); + assert!(!pt.dirty(1)); + assert_eq!(pt.map(2), false); pt.unmap(0); diff --git a/crate/memory/src/page_table/mod.rs b/crate/memory/src/page_table/mod.rs index 245311a..d10f652 100644 --- a/crate/memory/src/page_table/mod.rs +++ b/crate/memory/src/page_table/mod.rs @@ -5,6 +5,8 @@ pub mod mock_page_table; pub trait PageTable { fn accessed(&self, addr: VirtAddr) -> bool; fn dirty(&self, addr: VirtAddr) -> bool; + fn clear_accessed(&mut self, addr: VirtAddr); + fn clear_dirty(&mut self, addr: VirtAddr); fn map(&mut self, addr: VirtAddr) -> bool; fn unmap(&mut self, addr: VirtAddr); } \ No newline at end of file diff --git a/crate/memory/src/swap/enhanced_clock.rs b/crate/memory/src/swap/enhanced_clock.rs new file mode 100644 index 0000000..ea782a5 --- /dev/null +++ b/crate/memory/src/swap/enhanced_clock.rs @@ -0,0 +1,135 @@ +use alloc::vec_deque::VecDeque; +use super::*; + +pub struct EnhancedClockSwapManager { + page_table: &'static mut T, + clock_ptr: usize, + deque: VecDeque, +} + +impl SwapManager for EnhancedClockSwapManager { + fn tick(&mut self) { + } + + fn push(&mut self, addr: usize) { + let pos = if self.clock_ptr == 0 {self.deque.len()} else {self.clock_ptr}; + self.deque.insert(pos, addr); + } + + fn remove(&mut self, addr: usize) { + let id = self.deque.iter() + .position(|&x| x == addr) + .expect("address not found"); + if id < self.clock_ptr { + self.clock_ptr -= 1; + } + self.deque.remove(id); + } + + fn pop(&mut self) -> Option { + loop { + let addr = self.deque[self.clock_ptr]; + let accessed = self.page_table.accessed(addr); + let dirty = self.page_table.dirty(addr); + + match (accessed, dirty) { + (true, _) => { + self.page_table.clear_accessed(addr); + + }, + (false, true) => { + if self.page_table.swap_out(addr).is_ok() { + self.page_table.clear_dirty(addr); + } + }, + _ => { + return self.remove_current(); + } + } + self.move_next(); + } + } +} + + +impl EnhancedClockSwapManager { + pub fn new(page_table: &'static mut T) -> Self { + EnhancedClockSwapManager { + page_table, + clock_ptr: 0, + deque: VecDeque::::new() + } + } + fn remove_current(&mut self) -> Option { + let addr = self.deque.remove(self.clock_ptr); + if self.clock_ptr == self.deque.len() { + self.clock_ptr = 0; + } + return addr; + } + fn move_next(&mut self) { + self.clock_ptr += 1; + if self.clock_ptr == self.deque.len() { + self.clock_ptr = 0; + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::{arc::Arc, boxed::Box}; + use core::mem::uninitialized; + use core::cell::RefCell; + use page_table::mock_page_table::MockPageTable; + + impl SwappablePageTable for MockPageTable { + fn swap_out(&mut self, addr: usize) -> Result<(), ()> { + Ok(()) + } + } + + enum MemOp { + R(usize), W(usize) + } + + #[test] + fn test() { + use self::MemOp::{R, W}; + let page_fault_count = Arc::new(RefCell::new(0usize)); + + let mut pt = Box::new(MockPageTable::new(4)); + let static_pt = unsafe{ &mut *(pt.as_mut() as *mut MockPageTable) }; + pt.set_handler(Box::new({ + let page_fault_count1 = page_fault_count.clone(); + let mut clock = EnhancedClockSwapManager::new(static_pt); + + move |pt: &mut MockPageTable, addr: VirtAddr| { + *page_fault_count1.borrow_mut() += 1; + + if !pt.map(addr) { // is full? + pt.unmap(clock.pop().unwrap()); + pt.map(addr); + } + clock.push(addr); + } + })); + + + let op_seq = [ + R(1), R(2), R(3), R(4), + R(3), W(1), R(4), W(2), R(5), + R(2), W(1), R(2), R(3), R(4)]; + let pgfault_count = [ + 1, 2, 3, 4, + 4, 4, 4, 4, 5, + 5, 5, 5, 6, 7]; + for (op, &count) in op_seq.iter().zip(pgfault_count.iter()) { + match op { + R(addr) => pt.read(*addr), + W(addr) => pt.write(*addr), + } + assert_eq!(*(*page_fault_count).borrow(), count); + } + } +} diff --git a/crate/memory/src/swap/fifo.rs b/crate/memory/src/swap/fifo.rs index daa796f..ed63980 100644 --- a/crate/memory/src/swap/fifo.rs +++ b/crate/memory/src/swap/fifo.rs @@ -1,7 +1,7 @@ use alloc::vec_deque::VecDeque; use super::*; -struct FifoSwapManager { +pub struct FifoSwapManager { deque: VecDeque, } @@ -27,7 +27,7 @@ impl SwapManager for FifoSwapManager { } impl FifoSwapManager { - fn new() -> Self { + pub fn new() -> Self { FifoSwapManager { deque: VecDeque::::new() } @@ -50,7 +50,8 @@ mod test { use self::MemOp::{R, W}; let page_fault_count = Arc::new(RefCell::new(0usize)); - let mut pt = MockPageTable::new(4, Box::new({ + let mut pt = MockPageTable::new(4); + pt.set_handler(Box::new({ let page_fault_count1 = page_fault_count.clone(); let mut fifo = FifoSwapManager::new(); diff --git a/crate/memory/src/swap/mock_swapper.rs b/crate/memory/src/swap/mock_swapper.rs index af8d8ba..f1b9704 100644 --- a/crate/memory/src/swap/mock_swapper.rs +++ b/crate/memory/src/swap/mock_swapper.rs @@ -6,14 +6,25 @@ pub struct MockSwapper { } impl Swapper for MockSwapper { - fn swap_out(&mut self, data: &[u8; 4096]) -> usize { + fn swap_out(&mut self, data: &[u8; 4096]) -> Result { let id = self.alloc_id(); self.map.insert(id, data.clone()); - id + Ok(id) } - fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) { - *data = self.map.remove(&token).unwrap(); + fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()> { + if !self.map.contains_key(&token) { + return Err(()); + } + self.map.insert(token, data.clone()); + Ok(()) + } + fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()> { + match self.map.remove(&token) { + Some(d) => *data = d, + None => return Err(()), + } + Ok(()) } } @@ -31,16 +42,25 @@ mod test { use super::*; use core::mem::uninitialized; + fn assert_data_eq(data1: &[u8; 4096], data2: &[u8; 4096]) { + for (&a, &b) in data2.iter().zip(data1.iter()) { + assert_eq!(a, b); + } + } + #[test] fn test() { let mut swapper = MockSwapper::new(); - let data: [u8; 4096] = unsafe{ uninitialized() }; - let mut data1: [u8; 4096] = unsafe{ uninitialized() }; - let token = swapper.swap_out(&data); - swapper.swap_in(token, &mut data1); - for (&a, &b) in data.iter().zip(data1.iter()) { - assert_eq!(a, b); - } + let mut data: [u8; 4096] = unsafe{ uninitialized() }; + let data1: [u8; 4096] = unsafe{ uninitialized() }; + let token = swapper.swap_out(&data1).unwrap(); + swapper.swap_in(token, &mut data); + assert_data_eq(&data, &data1); + + let data2: [u8; 4096] = unsafe{ uninitialized() }; + swapper.swap_update(token, &data2); + swapper.swap_in(token, &mut data); + assert_data_eq(&data, &data2); } #[test] diff --git a/crate/memory/src/swap/mod.rs b/crate/memory/src/swap/mod.rs index 6adaa6b..5b9c108 100644 --- a/crate/memory/src/swap/mod.rs +++ b/crate/memory/src/swap/mod.rs @@ -1,10 +1,14 @@ +pub use self::fifo::FifoSwapManager; +pub use self::enhanced_clock::EnhancedClockSwapManager; + use super::*; use super::page_table::PageTable; -pub mod fifo; +mod fifo; +mod enhanced_clock; mod mock_swapper; -trait SwapManager { +pub trait SwapManager { /// Called when tick interrupt occured fn tick(&mut self); /// Called when map a swappable page into the memory @@ -15,7 +19,12 @@ trait SwapManager { fn pop(&mut self) -> Option; } -trait Swapper { - fn swap_out(&mut self, data: &[u8; 4096]) -> usize; - fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]); +pub trait Swapper { + fn swap_out(&mut self, data: &[u8; 4096]) -> Result; + fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()>; + fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()>; +} + +pub trait SwappablePageTable: PageTable { + fn swap_out(&mut self, addr: VirtAddr) -> Result<(), ()>; } \ No newline at end of file