You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
16 KiB

//! Swap extension for page table
//! and generic interface for swap manager and swapper
//!
//! To use the SwapExt, make a wrapper over the original apge table using swap manager and swapper
//! Like: SwapExt::new(origin_page_table,swap_manager,swapper)
//! Invoke page_fault_handler() on the SwapExt to run the swap process
//! If the method above returns true, a page is swapped in, else do your own things.
use super::*;
use super::paging::*;
use super::memory_set::InactivePageTable;
use super::addr::Frame;
use alloc::rc::Rc;
use core::ops::{Deref, DerefMut};
//pub use self::fifo::FifoSwapManager;
//pub use self::enhanced_clock::EnhancedClockSwapManager;
pub mod fifo;
//mod enhanced_clock;
pub mod mock_swapper;
//#[cfg(test)]
//mod mock_swapper;
/// Manage all swappable pages, decide which to swap out
pub trait SwapManager {
//type Inactive: InactivePageTable;
/*
** @brief update intarnal state pre tick
** Called when tick interrupt occured
** @retval none
*/
fn tick(&mut self);
/*
** @brief update intarnal state when page is pushed into memory
** Called when map a swappable page into the memory
** @param frame: Frame the Frame recording the swappable frame info
** @retval none
*/
fn push(&mut self, frame: Frame);
/*
** @brief update intarnal state when page is removed from memory
** Called to delete the addr entry from the swap manager
** @param token: usize the inactive page table token for the virtual address
** @param addr: VirtAddr the virual address of the page removed from memory
** @retval none
*/
fn remove(&mut self, token: usize, addr: VirtAddr);
/*
** @brief select swap out victim when there is need to swap out a page
** (The params is only used by `EnhancedClockSwapManager` currently)
** @param page_table: &mut T the current page table
** @param swapper: &mut S the swapper used
** @retval Option<Frame> the Frame of the victim page, if present
*/
fn pop<T, S>(&mut self, page_table: &mut T, swapper: &mut S) -> Option<Frame>
where T: PageTable, S: Swapper;
}
/// Implement swap in & out execution
pub trait Swapper {
/*
** @brief Allocate space on device and write data to it
** @param data: &[u8] the data to write to the device
** @retval Result<usize, ()> the execute result, and a token indicating the location on the device if success
*/
fn swap_out(&mut self, data: &[u8]) -> Result<usize, ()>;
/*
** @brief Update data on device.
** @param token: usize the token indicating the location on the device
** @param data: &[u8] the data to overwrite on the device
** @retval Result<(), ()> the execute result
*/
fn swap_update(&mut self, token: usize, data: &[u8]) -> Result<(), ()>;
/*
** @brief Recover data from device and deallocate the space.
** @param token: usize the token indicating the location on the device
** @param data: &mut [u8] the reference to data in the space in memory
** @retval Result<(), ()> the execute result
*/
fn swap_in(&mut self, token: usize, data: &mut [u8]) -> Result<(), ()>;
}
/// Wrapper for page table, supporting swap functions
pub struct SwapExt<T: PageTable, M: SwapManager, S: Swapper> {
page_table: T,
swap_manager: M,
swapper: S,
}
impl<T: PageTable, M: SwapManager, S: Swapper> SwapExt<T, M, S> {
/*
** @brief create a swap extension
** @param page_table: T the inner page table
** @param swap_manager: M the SwapManager used
** @param swapper: S the Swapper used
** @retval SwapExt the swap extension created
*/
pub fn new(page_table: T, swap_manager: M, swapper: S) -> Self {
SwapExt {
page_table,
swap_manager,
swapper,
}
}
/*
** @brief set a page swappable
** @param pt: *mut T2 the raw pointer for the target page's inactive page table
** @param addr: VirtAddr the target page's virtual address
*/
pub unsafe fn set_swappable<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr){
let Self {ref mut page_table, ref mut swap_manager, ref mut swapper} = self;
let targetpt = &mut *(pt);
let pttoken = {
info!("SET_SWAPPABLE: the target page table token is {:x?}, addr is {:x?}", targetpt.token(), addr);
targetpt.token()
};
targetpt.with(||{
let entry = page_table.get_entry(addr);
if entry.present() {
let frame = Frame::new(pt as usize, addr, pttoken);
swap_manager.push(frame);
}
});
/*
let token = unsafe{
(*pt).token()
};
let frame = Frame::new(pt as usize, addr, token);
self.swap_manager.push(frame);
*/
}
/*
** @brief remove a page (given virtual address) from swappable pages, if the page is swapped, swap in at first
** @param pt: *mut T2 the raw pointer for the target page's inactive page table
** @param addr: VirtAddr the target page's virtual address
** @param alloc_frame: the function to alloc a free physical frame for once
*/
pub unsafe fn remove_from_swappable<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr, alloc_frame: impl FnOnce() -> PhysAddr){
info!("come into remove_from swappable");
let Self {ref mut page_table, ref mut swap_manager, ref mut swapper} = self;
let targetpt = &mut *(pt);
let pttoken = {
debug!("the target page table token is {:x?}", targetpt.token());
targetpt.token()
};
info!("try to change pagetable");
targetpt.with(||{
let token = {
let entry = page_table.get_entry(addr);
if !entry.swapped() {
if entry.present(){
// if the addr isn't indicating a swapped page, panic occured here
swap_manager.remove(pttoken, addr);
}
return;
}
let token = entry.target() / PAGE_SIZE;
let frame = alloc_frame();
entry.set_target(frame);
entry.set_swapped(false);
entry.set_present(true);
entry.update();
token
};
info!("swap in vaddr {:x?} at remove from swappable.", addr);
let data = page_table.get_page_slice_mut(addr);
swapper.swap_in(token, data).unwrap();
});
trace!("come out of femove_from swappable");
}
/*
** @brief map the virtual address to a target physics address as swappable
** @param addr: VirtAddr the virual address to map
** @param target: VirtAddr the target physics address
** @retval none
*/
/*
pub fn map_to_swappable(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut T::Entry {
self.swap_manager.push(addr);
self.map(addr, target)
}*/
/*
** @brief Swap out any one of the swapped pages
** @retval Result<PhysAddr, SwapError>
** the physics address of released frame if success,
** the error if failed
*/
pub fn swap_out_any<T2: InactivePageTable>(&mut self) -> Result<PhysAddr, SwapError> {
info!("COME in to swap_out_any");
let victim: Option<Frame> = {
let Self {ref mut page_table, ref mut swap_manager, ref mut swapper} = self;
swap_manager.pop(page_table, swapper)
};
info!("swap out page {}", victim.unwrap().get_virtaddr());
match victim {
None => Err(SwapError::NoSwapped),
Some(frame) => self.swap_out::<T2>(&frame),
}
}
/*
** @brief Swap out page
** @param frame: Frame the Frame of page recording the page info
** @retval Result<PhysAddr, SwapError>
** the physics address of the original map target frame if success,
** the error if failed
*/
fn swap_out<T2: InactivePageTable>(&mut self, frame: &Frame) -> Result<PhysAddr, SwapError> {
let Self {ref mut page_table, ref mut swap_manager, ref mut swapper} = self;
let ret = unsafe{
let pt = &mut *(frame.get_page_table() as *mut T2);
pt.with_retval(||{
//use core::slice;
//let data = unsafe { slice::from_raw_parts_mut((frame.virtaddr & !(PAGE_SIZE - 1)) as *mut u8, PAGE_SIZE) };
let data = page_table.get_page_slice_mut(frame.get_virtaddr());
let entry = page_table.get_entry(frame.get_virtaddr());
if entry.swapped() {
return Err(SwapError::AlreadySwapped);
}
//assert!(!entry.swapped(), "Page already swapped!");
let token = swapper.swap_out(data).map_err(|_| SwapError::IOError)?;
//let token = swapper.swap_out(data).unwrap();
let target = entry.target();
entry.set_target(token * PAGE_SIZE);
entry.set_swapped(true);
entry.set_present(false);
entry.update();
Ok(target)
})
};
ret
}
/*
** @brief map the virtual address to a target physics address and then swap in page data, noted that the page should be in the current page table
** @param pt: *mut T2 the raw pointer for the swapping page's inactive page table
** @param addr: VirtAddr the virual address of beginning of page
** @param target: PhysAddr the target physics address
** @retval Result<()), SwapError>
** the execute result, and the error if failed
*/
fn swap_in<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr, target: PhysAddr) -> Result<(), SwapError> {
info!("come in to swap in");
let token = {
let entry = self.page_table.get_entry(addr);
if !entry.swapped() {
return Err(SwapError::NotSwapped);
}
let token = entry.target() / PAGE_SIZE;
entry.set_target(target);
entry.set_swapped(false);
entry.set_present(true);
entry.update();
token
};
let data = self.page_table.get_page_slice_mut(addr);
self.swapper.swap_in(token, data).map_err(|_| SwapError::IOError)?;
let pttoken = unsafe{
(*pt).token()
};
let frame = Frame::new(pt as usize, addr, pttoken);
;
self.swap_manager.push(frame);
Ok(())
}
/*
** @brief execute the frame delayed allocate and swap process for page fault
** This function must be called whenever PageFault happens.
** @param pt: *mut T2 the raw pointer for the target page's inactive page table (exactly the current page table)
** @param addr: VirtAddr the virual address of the page fault
** @param swapin: bool whether to set the page swappable if delayed allocate a frame for a page
** @param alloc_frame: impl FnOnce() -> PhysAddr
** the page allocation function
** that allocate a page and returns physics address
** of beginning of the page
** @retval bool whether swap in happens.
*/
pub fn page_fault_handler<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr, swapin: bool, alloc_frame: impl FnOnce() -> PhysAddr) -> bool {
// handle page delayed allocating
{
info!("try handling delayed frame allocator");
let need_alloc ={
let entry = self.page_table.get_entry(addr);
//info!("got entry!");
!entry.present() && !entry.swapped()
};
info!("need_alloc got");
if need_alloc {
info!("need_alloc!");
let frame = alloc_frame();
{
let entry = self.page_table.get_entry(addr);
entry.set_target(frame);
//let new_entry = self.page_table.map(addr, frame);
entry.set_present(true);
entry.update();
}
if(swapin){
unsafe {
self.set_swappable(pt, addr & 0xfffff000);
}
}
//area.get_flags().apply(new_entry); this instruction may be used when hide attr is used
info!("allocated successfully");
return true;
}
info!("not need alloc!");
}
// now we didn't attach the cow so the present will be false when swapped(), to enable the cow some changes will be needed
if !self.page_table.get_entry(addr).swapped() {
return false;
}
// Allocate a frame, if failed, swap out a page
let frame = alloc_frame();
self.swap_in(pt, addr, frame).ok().unwrap();
true
}
}
pub enum SwapError {
/// attempt to swap out a page that is already swapped out
AlreadySwapped,
/// attempt to swap in a page that is already in the memory
NotSwapped,
/// there are no page to be swapped out
NoSwapped,
/// swap failed due to IO error while interact with device
IOError,
}
impl<T: PageTable, M: SwapManager, S: Swapper> Deref for SwapExt<T, M, S> {
type Target = T;
fn deref(&self) -> &<Self as Deref>::Target {
&self.page_table
}
}
impl<T: PageTable, M: SwapManager, S: Swapper> DerefMut for SwapExt<T, M, S> {
fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
&mut self.page_table
}
}
/*
#[cfg(test)]
mod test {
use super::*;
use super::mock_swapper::MockSwapper;
use alloc::{sync::Arc, boxed::Box};
use core::cell::RefCell;
use paging::MockPageTable;
#[derive(Debug)]
pub enum MemOp {
R(usize),
W(usize),
}
struct FrameAlloc(usize);
impl FrameAlloc {
fn alloc(&mut self) -> Option<PhysAddr> {
if self.0 == 0 {
return None;
}
self.0 -= 1;
Some((self.0 + 1) * PAGE_SIZE)
}
}
unsafe fn clone<'a, 'b, T>(x: &'a mut T) -> &'b mut T {
&mut *(x as *mut T)
}
/// Test framework with different SwapManagers.
/// See `fifo::test` mod for example.
pub fn test_manager(swap_manager: impl 'static + SwapManager, ops: &[MemOp], pgfault_count: &[u8]) {
use self::MemOp::{R, W};
let page_fault_count = Arc::new(RefCell::new(0u8));
let mut pt = SwapExt::new(MockPageTable::new(), swap_manager, MockSwapper::default());
// Move to closure
let pt0 = unsafe{ clone(&mut pt) };
let page_fault_count1 = page_fault_count.clone();
let mut alloc = FrameAlloc(4);
pt.set_handler(Box::new(move |_, addr: VirtAddr| {
*page_fault_count1.borrow_mut() += 1;
if pt0.page_fault_handler(addr, || alloc.alloc()) {
return;
}
// The page is not mapped, map it to a new frame, if no more frame, swap out.
let target = alloc.alloc().or_else(|| pt0.swap_out_any().ok())
.expect("no more frame in both allocator and swap_manager");
pt0.map_to_swappable(addr, target);
}));
for (op, &count) in ops.iter().zip(pgfault_count.iter()) {
match op {
R(addr) => { pt.read(*addr); }
W(addr) => pt.write(*addr, 0),
}
assert_eq!(*(*page_fault_count).borrow(), count);
}
}
}
*/