EnhancedClockSwapManager

master
WangRunji 7 years ago
parent c0d83ef75b
commit 733c102836

@ -5,7 +5,7 @@ pub struct MockPageTable {
mapped_set: BTreeSet<VirtAddr>, mapped_set: BTreeSet<VirtAddr>,
accessed_set: BTreeSet<VirtAddr>, accessed_set: BTreeSet<VirtAddr>,
dirty_set: BTreeSet<VirtAddr>, dirty_set: BTreeSet<VirtAddr>,
page_fault_handler: PageFaultHandler, page_fault_handler: Option<PageFaultHandler>,
capacity: usize, capacity: usize,
} }
@ -18,6 +18,12 @@ impl PageTable for MockPageTable {
fn dirty(&self, addr: VirtAddr) -> bool { fn dirty(&self, addr: VirtAddr) -> bool {
self.dirty_set.contains(&addr) 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 /// Map a page, return false if no more space
fn map(&mut self, addr: VirtAddr) -> bool { fn map(&mut self, addr: VirtAddr) -> bool {
if self.mapped_set.len() == self.capacity { if self.mapped_set.len() == self.capacity {
@ -32,30 +38,33 @@ impl PageTable for MockPageTable {
} }
impl MockPageTable { impl MockPageTable {
pub fn new(capacity: usize, page_fault_handler: PageFaultHandler) -> Self { pub fn new(capacity: usize) -> Self {
MockPageTable { MockPageTable {
mapped_set: BTreeSet::<VirtAddr>::new(), mapped_set: BTreeSet::<VirtAddr>::new(),
accessed_set: BTreeSet::<VirtAddr>::new(), accessed_set: BTreeSet::<VirtAddr>::new(),
dirty_set: BTreeSet::<VirtAddr>::new(), dirty_set: BTreeSet::<VirtAddr>::new(),
page_fault_handler, page_fault_handler: None,
capacity, capacity,
} }
} }
/// Read memory, mark accessed, trigger page fault if not present pub fn set_handler(&mut self, page_fault_handler: PageFaultHandler) {
pub fn read(&mut self, addr: VirtAddr) { 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) { while !self.mapped_set.contains(&addr) {
let self_mut = unsafe{ &mut *(self as *mut Self) }; 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); self.accessed_set.insert(addr);
} }
/// Write memory, mark accessed and dirty, trigger page fault if not present /// Write memory, mark accessed and dirty, trigger page fault if not present
pub fn write(&mut self, addr: VirtAddr) { pub fn write(&mut self, addr: VirtAddr) {
while !self.mapped_set.contains(&addr) { self.trigger_page_fault_if_not_present(addr);
let self_mut = unsafe{ &mut *(self as *mut Self) };
(self.page_fault_handler)(self_mut, addr);
}
self.accessed_set.insert(addr); self.accessed_set.insert(addr);
self.dirty_set.insert(addr); self.dirty_set.insert(addr);
} }
@ -71,7 +80,8 @@ mod test {
fn test() { fn test() {
let page_fault_count = Arc::new(RefCell::new(0usize)); 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(); let page_fault_count1 = page_fault_count.clone();
move |pt: &mut MockPageTable, addr: VirtAddr| { move |pt: &mut MockPageTable, addr: VirtAddr| {
*page_fault_count1.borrow_mut() += 1; *page_fault_count1.borrow_mut() += 1;
@ -85,11 +95,17 @@ mod test {
assert!(pt.accessed(0)); assert!(pt.accessed(0));
assert!(!pt.dirty(0)); assert!(!pt.dirty(0));
pt.clear_accessed(0);
assert!(!pt.accessed(0));
pt.write(1); pt.write(1);
assert_eq!(*page_fault_count.borrow(), 1); assert_eq!(*page_fault_count.borrow(), 1);
assert!(pt.accessed(1)); assert!(pt.accessed(1));
assert!(pt.dirty(1)); assert!(pt.dirty(1));
pt.clear_dirty(1);
assert!(!pt.dirty(1));
assert_eq!(pt.map(2), false); assert_eq!(pt.map(2), false);
pt.unmap(0); pt.unmap(0);

@ -5,6 +5,8 @@ pub mod mock_page_table;
pub trait PageTable { pub trait PageTable {
fn accessed(&self, addr: VirtAddr) -> bool; fn accessed(&self, addr: VirtAddr) -> bool;
fn dirty(&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 map(&mut self, addr: VirtAddr) -> bool;
fn unmap(&mut self, addr: VirtAddr); fn unmap(&mut self, addr: VirtAddr);
} }

@ -0,0 +1,135 @@
use alloc::vec_deque::VecDeque;
use super::*;
pub struct EnhancedClockSwapManager<T: 'static + SwappablePageTable> {
page_table: &'static mut T,
clock_ptr: usize,
deque: VecDeque<VirtAddr>,
}
impl<T: 'static + SwappablePageTable> SwapManager for EnhancedClockSwapManager<T> {
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<usize> {
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<T: 'static + SwappablePageTable> EnhancedClockSwapManager<T> {
pub fn new(page_table: &'static mut T) -> Self {
EnhancedClockSwapManager {
page_table,
clock_ptr: 0,
deque: VecDeque::<VirtAddr>::new()
}
}
fn remove_current(&mut self) -> Option<VirtAddr> {
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);
}
}
}

@ -1,7 +1,7 @@
use alloc::vec_deque::VecDeque; use alloc::vec_deque::VecDeque;
use super::*; use super::*;
struct FifoSwapManager { pub struct FifoSwapManager {
deque: VecDeque<VirtAddr>, deque: VecDeque<VirtAddr>,
} }
@ -27,7 +27,7 @@ impl SwapManager for FifoSwapManager {
} }
impl FifoSwapManager { impl FifoSwapManager {
fn new() -> Self { pub fn new() -> Self {
FifoSwapManager { FifoSwapManager {
deque: VecDeque::<VirtAddr>::new() deque: VecDeque::<VirtAddr>::new()
} }
@ -50,7 +50,8 @@ mod test {
use self::MemOp::{R, W}; use self::MemOp::{R, W};
let page_fault_count = Arc::new(RefCell::new(0usize)); 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 page_fault_count1 = page_fault_count.clone();
let mut fifo = FifoSwapManager::new(); let mut fifo = FifoSwapManager::new();

@ -6,14 +6,25 @@ pub struct MockSwapper {
} }
impl Swapper for MockSwapper { impl Swapper for MockSwapper {
fn swap_out(&mut self, data: &[u8; 4096]) -> usize { fn swap_out(&mut self, data: &[u8; 4096]) -> Result<usize, ()> {
let id = self.alloc_id(); let id = self.alloc_id();
self.map.insert(id, data.clone()); self.map.insert(id, data.clone());
id Ok(id)
} }
fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) { fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()> {
*data = self.map.remove(&token).unwrap(); 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 super::*;
use core::mem::uninitialized; 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] #[test]
fn test() { fn test() {
let mut swapper = MockSwapper::new(); let mut swapper = MockSwapper::new();
let data: [u8; 4096] = unsafe{ uninitialized() }; let mut data: [u8; 4096] = unsafe{ uninitialized() };
let mut data1: [u8; 4096] = unsafe{ uninitialized() }; let data1: [u8; 4096] = unsafe{ uninitialized() };
let token = swapper.swap_out(&data); let token = swapper.swap_out(&data1).unwrap();
swapper.swap_in(token, &mut data1); swapper.swap_in(token, &mut data);
for (&a, &b) in data.iter().zip(data1.iter()) { assert_data_eq(&data, &data1);
assert_eq!(a, b);
} let data2: [u8; 4096] = unsafe{ uninitialized() };
swapper.swap_update(token, &data2);
swapper.swap_in(token, &mut data);
assert_data_eq(&data, &data2);
} }
#[test] #[test]

@ -1,10 +1,14 @@
pub use self::fifo::FifoSwapManager;
pub use self::enhanced_clock::EnhancedClockSwapManager;
use super::*; use super::*;
use super::page_table::PageTable; use super::page_table::PageTable;
pub mod fifo; mod fifo;
mod enhanced_clock;
mod mock_swapper; mod mock_swapper;
trait SwapManager { pub trait SwapManager {
/// Called when tick interrupt occured /// Called when tick interrupt occured
fn tick(&mut self); fn tick(&mut self);
/// Called when map a swappable page into the memory /// Called when map a swappable page into the memory
@ -15,7 +19,12 @@ trait SwapManager {
fn pop(&mut self) -> Option<VirtAddr>; fn pop(&mut self) -> Option<VirtAddr>;
} }
trait Swapper { pub trait Swapper {
fn swap_out(&mut self, data: &[u8; 4096]) -> usize; fn swap_out(&mut self, data: &[u8; 4096]) -> Result<usize, ()>;
fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]); 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<(), ()>;
} }
Loading…
Cancel
Save