Finish swap for multi-user process

master
lcy1996 6 years ago
parent ec27ed8d2e
commit edde416052

@ -4,3 +4,4 @@ version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>"]
[dependencies]
log = "0.4"

@ -1,4 +1,6 @@
use core::ops::{Add, AddAssign};
use super::paging::*;
use super::memory_set::*;
pub type VirtAddr = usize;
pub type PhysAddr = usize;
@ -74,3 +76,44 @@ impl Iterator for PageRange {
}
}
}
/// frame for the swapmanager
#[derive(Debug, Copy, Clone, PartialOrd, Ord)]
#[repr(C)]
pub struct Frame {
page_table: usize, // the raw pointer for the frame's memory set's inactive page table
virtaddr: VirtAddr, // the virtual addr for the frame
token: usize, // the token for frame
}
impl Frame{
pub fn get_page_table(&self) -> usize {
self.page_table
}
pub fn get_virtaddr(&self) -> VirtAddr {
self.virtaddr
}
pub fn get_token(&self) -> usize{
self.token
}
pub fn new(pt: usize, addr: VirtAddr, pttoken: usize) -> Self{
Frame {
page_table: pt,
virtaddr: addr,
token: pttoken,
}
}
}
impl PartialEq for Frame{
fn eq(&self, other: &Frame) -> bool {
self.token == other.token && self.virtaddr == other.virtaddr
}
}
impl Eq for Frame{
}

@ -2,6 +2,8 @@
#![feature(alloc)]
extern crate alloc;
#[macro_use]
extern crate log;
// To use `println!` in test
#[cfg(test)]

@ -66,6 +66,8 @@ pub trait InactivePageTable {
** @retval Stack the stack allocated
*/
fn alloc_stack() -> Stack;
unsafe fn with_retval<T>(&self, f: impl FnOnce() -> T) -> T;
}
/// a continuous memory space when the same attribute
@ -367,6 +369,14 @@ impl<T: InactivePageTable> MemorySet<T> {
});
areas.clear();
}
/*
** @brief get the mutable reference for the inactive page table
** @retval: &mut T the mutable reference of the inactive page table
*/
pub fn get_page_table_mut(&mut self) -> &mut T{
&mut self.page_table
}
}
impl<T: InactivePageTable> Clone for MemorySet<T> {
@ -387,6 +397,7 @@ impl<T: InactivePageTable> Clone for MemorySet<T> {
impl<T: InactivePageTable> Drop for MemorySet<T> {
fn drop(&mut self) {
info!("come into drop func for memoryset");
self.clear();
}
}

@ -3,6 +3,7 @@
//! Implemented for every architecture, used by OS.
use super::*;
use super::memory_set::InactivePageTable;
#[cfg(test)]
pub use self::mock_page_table::MockPageTable;

@ -3,32 +3,36 @@
use alloc::collections::VecDeque;
use super::*;
#[derive(Default)]
pub struct FifoSwapManager {
deque: VecDeque<VirtAddr>,
pub struct FifoSwapManager {
deque: VecDeque<Frame>,
}
impl SwapManager for FifoSwapManager {
fn tick(&mut self) {}
fn push(&mut self, addr: usize) {
self.deque.push_back(addr);
fn push(&mut self, frame: Frame) {
debug!("SwapManager push token: {:x?} vaddr: {:x?}", frame.get_token(), frame.get_virtaddr());
self.deque.push_back(frame);
}
fn remove(&mut self, addr: usize) {
fn remove(&mut self, token: usize, addr: VirtAddr) {
debug!("SwapManager remove token: {:x?} vaddr: {:x?}", token, addr);
let id = self.deque.iter()
.position(|&x| x == addr)
.position(|ref x| x.get_virtaddr() == addr && x.get_token() == token)
.expect("address not found");
self.deque.remove(id);
}
fn pop<T, S>(&mut self, _: &mut T, _: &mut S) -> Option<VirtAddr>
fn pop<T, S>(&mut self, _: &mut T, _: &mut S) -> Option<Frame>
where T: PageTable, S: Swapper
{
self.deque.pop_front()
}
}
/*
#[cfg(test)]
mod test {
use super::*;
@ -50,3 +54,4 @@ mod test {
test_manager(FifoSwapManager::default(), &ops, &pgfault_count);
}
}
*/

@ -4,7 +4,6 @@
//! Used to test page table operation
use super::Swapper;
//use alloc::btree_map::BTreeMap;
use alloc::collections::BTreeMap;
use core::mem::uninitialized;

@ -8,19 +8,23 @@
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 use self::enhanced_clock::EnhancedClockSwapManager;
pub mod fifo;
mod enhanced_clock;
//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
@ -30,25 +34,26 @@ pub trait SwapManager {
/*
** @brief update intarnal state when page is pushed into memory
** Called when map a swappable page into the memory
** @param addr: VirtAddr the virual address of the page pushed into memory
** @param frame: Frame the Frame recording the swappable frame info
** @retval none
*/
fn push(&mut self, addr: VirtAddr);
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, addr: VirtAddr);
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<VirtAddr> the virual address of the victim page, if present
** @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<VirtAddr>
fn pop<T, S>(&mut self, page_table: &mut T, swapper: &mut S) -> Option<Frame>
where T: PageTable, S: Swapper;
}
@ -100,11 +105,52 @@ impl<T: PageTable, M: SwapManager, S: Swapper> SwapExt<T, M, S> {
}
/*
** @brief set a virtual address (a page) swappable
** @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 fn set_swappable(&mut self, addr: VirtAddr){
self.swap_manager.push(addr);
pub fn set_swappable<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr){
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){
trace!("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()
};
debug!("try to change pagetable");
let targetaddr = targetpt.with(||{
let token = {
let entry = page_table.get_entry(addr);
if !entry.swapped() {
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
};
let data = page_table.get_page_slice_mut(addr);
swapper.swap_in(token, data).unwrap();
});
trace!("come outof femove_from swappable");
}
/*
@ -113,55 +159,72 @@ impl<T: PageTable, M: SwapManager, S: Swapper> SwapExt<T, M, S> {
** @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(&mut self) -> Result<PhysAddr, SwapError> {
let victim = {
pub fn swap_out_any<T2: InactivePageTable>(&mut self) -> Result<PhysAddr, SwapError> {
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(addr) => self.swap_out(addr),
Some(frame) => self.swap_out::<T2>(&frame),
}
}
/*
** @brief Swap out page
** @param addr: VirtAddr the virual address of beginning of 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(&mut self, addr: VirtAddr) -> Result<PhysAddr, SwapError> {
let data = self.page_table.get_page_slice_mut(addr);
let entry = self.page_table.get_entry(addr);
if entry.swapped() {
return Err(SwapError::AlreadySwapped);
}
let token = self.swapper.swap_out(data).map_err(|_| SwapError::IOError)?;
let target = entry.target();
entry.set_target(token * PAGE_SIZE);
entry.set_swapped(true);
entry.set_present(false);
entry.update();
Ok(target)
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
** @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 addr: PhysAddr the target physics address
** @param target: PhysAddr the target physics address
** @retval Result<()), SwapError>
** the execute result, and the error if failed
*/
fn swap_in(&mut self, addr: VirtAddr, target: PhysAddr) -> Result<(), SwapError> {
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() {
@ -176,12 +239,18 @@ impl<T: PageTable, M: SwapManager, S: Swapper> SwapExt<T, M, S> {
};
let data = self.page_table.get_page_slice_mut(addr);
self.swapper.swap_in(token, data).map_err(|_| SwapError::IOError)?;
self.swap_manager.push(addr);
let pttoken = unsafe{
(*pt).token()
};
let frame = Frame::new(pt as usize, addr, pttoken);
;
self.swap_manager.push(frame);
Ok(())
}
/*
** @brief execute the 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 alloc_frame: impl FnOnce() -> PhysAddr
** the page allocation function
@ -189,14 +258,13 @@ impl<T: PageTable, M: SwapManager, S: Swapper> SwapExt<T, M, S> {
** of beginning of the page
** @retval bool whether swap in happens.
*/
pub fn page_fault_handler(&mut self, addr: VirtAddr, alloc_frame: impl FnOnce() -> PhysAddr) -> bool {
pub fn page_fault_handler<T2: InactivePageTable>(&mut self, pt: *mut T2, addr: VirtAddr, alloc_frame: impl FnOnce() -> PhysAddr) -> bool {
if !self.page_table.get_entry(addr).swapped() {
return false;
}
// Allocate a frame, if failed, swap out a page
//let frame = alloc_frame().unwrap_or_else(|| self.swap_out_any().ok().unwrap());
let frame = alloc_frame();
self.swap_in(addr, frame).ok().unwrap();
self.swap_in(pt, addr, frame).ok().unwrap();
true
}
}
@ -226,6 +294,7 @@ impl<T: PageTable, M: SwapManager, S: Swapper> DerefMut for SwapExt<T, M, S> {
}
}
/*
#[cfg(test)]
mod test {
use super::*;
@ -289,3 +358,4 @@ mod test {
}
}
}
*/

@ -257,6 +257,11 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
&self.get(self.current_pid).context
}
pub fn current_context_mut(&mut self) -> &mut T {
let id = self.current_pid;
&mut self.get_mut(id).context
}
/*
** @brief get pid of current process
** @param none

3
kernel/Cargo.lock generated

@ -235,6 +235,9 @@ dependencies = [
[[package]]
name = "ucore-memory"
version = "0.1.0"
dependencies = [
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ucore-process"

@ -238,9 +238,9 @@ impl InactivePageTable for InactivePageTable0 {
/*
* @param:
* f: a function to do something for a muatable activepagetable
* f: a function to do something with the temporary modified activate page table
* @brief:
* temporarily map the pagetable as an active page and apply f on the it
* temporarily map the inactive pagetable as an active p2page and apply f on the temporary modified active page table
*/
fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) {
active_table().with_temporary_map(&satp::read().frame(), |active_table, p2_table: &mut RvPageTable| {
@ -259,6 +259,10 @@ impl InactivePageTable for InactivePageTable0 {
});
}
/*
* @brief:
* active self as the current active page table
*/
unsafe fn activate(&self) {
let old_frame = satp::read().frame();
let new_frame = self.p2_frame.clone();
@ -269,6 +273,12 @@ impl InactivePageTable for InactivePageTable0 {
}
}
/*
* @param:
* f: the function to run when temporarily activate self as current page table
* @brief:
* Temporarily activate self and run the process
*/
unsafe fn with(&self, f: impl FnOnce()) {
let old_frame = satp::read().frame();
let new_frame = self.p2_frame.clone();
@ -285,6 +295,37 @@ impl InactivePageTable for InactivePageTable0 {
}
}
/*
* @param:
* f: the function to run when temporarily activate self as current page table
* @brief:
* Temporarily activate self and run the process, and return the return value of f
* @retval:
* the return value of f
*/
unsafe fn with_retval<T>(&self, f: impl FnOnce() -> T) -> T{
let old_frame = satp::read().frame();
let new_frame = self.p2_frame.clone();
debug!("switch table {:x?} -> {:x?}", old_frame, new_frame);
if old_frame != new_frame {
satp::set(satp::Mode::Sv32, 0, new_frame);
sfence_vma_all();
}
let target = f();
debug!("switch table {:x?} -> {:x?}", new_frame, old_frame);
if old_frame != new_frame {
satp::set(satp::Mode::Sv32, 0, old_frame);
sfence_vma_all();
}
target
}
/*
* @brief:
* get the token of self, the token is self's pagetable frame's starting physical address
* @retval:
* self token
*/
fn token(&self) -> usize {
self.p2_frame.number() | (1 << 31) // as satp
}

@ -69,8 +69,8 @@ pub fn kmain() -> ! {
// the test is not supported in riscv32(maybe)
//thread::test::local_key();
//thread::test::unpack();
sync::test::philosopher_using_mutex();
sync::test::philosopher_using_monitor();
//sync::test::philosopher_using_mutex();
//sync::test::philosopher_using_monitor();
//sync::mpsc::test::test_all();
// come into shell

@ -7,6 +7,8 @@ use ucore_memory::{*, paging::PageTable};
use ucore_memory::cow::CowExt;
pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, MemorySet as MemorySet_, Stack};
use ucore_memory::swap::*;
use alloc::collections::VecDeque;
use process::processor;
pub type MemorySet = MemorySet_<InactivePageTable0>;
@ -22,6 +24,7 @@ lazy_static! {
pub static ref FRAME_ALLOCATOR: Mutex<FrameAlloc> = Mutex::new(FrameAlloc::default());
}
lazy_static! {
static ref ACTIVE_TABLE: Mutex<CowExt<ActivePageTable>> = Mutex::new(unsafe {
CowExt::new(ActivePageTable::new())
@ -33,7 +36,7 @@ pub fn active_table() -> MutexGuard<'static, CowExt<ActivePageTable>> {
ACTIVE_TABLE.lock()
}
// Page table for swpa in and out
// Page table for swap in and out
lazy_static!{
static ref ACTIVE_TABLE_SWAP: Mutex<SwapExt<ActivePageTable, fifo::FifoSwapManager, mock_swapper::MockSwapper>> =
Mutex::new(unsafe{SwapExt::new(ActivePageTable::new(), fifo::FifoSwapManager::default(), mock_swapper::MockSwapper::default())});
@ -54,7 +57,7 @@ pub fn alloc_frame() -> Option<usize> {
let ret = FRAME_ALLOCATOR.lock().alloc().map(|id| id * PAGE_SIZE + MEMORY_OFFSET);
trace!("Allocate frame: {:x?}", ret);
//do we need : unsafe { ACTIVE_TABLE_SWAP.force_unlock(); } ???
Some(ret.unwrap_or_else(|| active_table_swap().swap_out_any().ok().unwrap()))
Some(ret.unwrap_or_else(|| active_table_swap().swap_out_any::<InactivePageTable0>().ok().unwrap()))
}
pub fn dealloc_frame(target: usize) {
@ -88,8 +91,11 @@ pub fn page_fault_handler(addr: usize) -> bool {
return true;
}
// handle the swap in/out
info!("start handling swap in/out page fault");
unsafe { ACTIVE_TABLE_SWAP.force_unlock(); }
if active_table_swap().page_fault_handler(addr, || alloc_frame().unwrap()){
let mut temp_proc = processor();
let pt = temp_proc.current_context_mut().get_memory_set_mut().get_page_table_mut();
if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, || alloc_frame().unwrap()){
return true;
}
false

@ -1,8 +1,9 @@
use arch::interrupt::{TrapFrame, Context as ArchContext};
use memory::{MemoryArea, MemoryAttr, MemorySet, active_table_swap};
use memory::{MemoryArea, MemoryAttr, MemorySet, active_table_swap, alloc_frame};
use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type}};
use core::fmt::{Debug, Error, Formatter};
use ucore_memory::{Page};
use ::memory::{InactivePageTable0};
pub struct Context {
arch: ArchContext,
@ -106,14 +107,9 @@ impl Context {
});
}
//set the user Memory pages in the memory set swappable
for area in memory_set.iter(){
for page in Page::range_of(area.get_start_addr(), area.get_end_addr()) {
let addr = page.start_address();
active_table_swap().set_swappable(addr);
}
}
info!("finish setting memory swappable.");
memory_set_map_swappable(&mut memory_set);
Context {
arch: unsafe {
@ -127,7 +123,7 @@ impl Context {
/// Fork
pub fn fork(&self, tf: &TrapFrame) -> Self {
// Clone memory set, make a new page table
let memory_set = self.memory_set.clone();
let mut memory_set = self.memory_set.clone();
// Copy data to temp space
use alloc::vec::Vec;
@ -143,12 +139,39 @@ impl Context {
}
});
}
// map the memory set swappable
memory_set_map_swappable(&mut memory_set);
Context {
arch: unsafe { ArchContext::new_fork(tf, memory_set.kstack_top(), memory_set.token()) },
memory_set,
}
}
pub fn get_memory_set_mut(&mut self) -> &mut MemorySet {
&mut self.memory_set
}
}
impl Drop for Context{
fn drop(&mut self){
//set the user Memory pages in the memory set unswappable
let Self {ref mut arch, ref mut memory_set} = self;
let pt = {
memory_set.get_page_table_mut() as *mut InactivePageTable0
};
for area in memory_set.iter(){
for page in Page::range_of(area.get_start_addr(), area.get_end_addr()) {
let addr = page.start_address();
unsafe {
active_table_swap().remove_from_swappable(pt, addr, || alloc_frame().unwrap());
}
}
}
info!("Finishing setting pages unswappable");
}
}
impl Debug for Context {
@ -187,3 +210,22 @@ fn memory_attr_from(elf_flags: Flags) -> MemoryAttr {
if elf_flags.is_execute() { flags = flags.execute(); }
flags
}
/*
* @param:
* memory_set: the target MemorySet to set swappable
* @brief:
* map the memory area in the memory_set swappalbe, specially for the user process
*/
fn memory_set_map_swappable(memory_set: &mut MemorySet){
let pt = unsafe {
memory_set.get_page_table_mut() as *mut InactivePageTable0
};
for area in memory_set.iter(){
for page in Page::range_of(area.get_start_addr(), area.get_end_addr()) {
let addr = page.start_address();
active_table_swap().set_swappable(pt, addr);
}
}
info!("Finishing setting pages swappable");
}
Loading…
Cancel
Save