spin::Mutex->UPSafeCell

This commit is contained in:
Yifan Wu 2021-07-20 22:10:22 +08:00
parent c82861f205
commit 3c9b6d7d14
26 changed files with 239 additions and 188 deletions

View file

@ -3,13 +3,22 @@ use crate::trap::trap_return;
#[repr(C)]
pub struct TaskContext {
ra: usize,
sp: usize,
s: [usize; 12],
}
impl TaskContext {
pub fn goto_trap_return() -> Self {
pub fn zero_init() -> Self {
Self {
ra: 0,
sp: 0,
s: [0; 12],
}
}
pub fn goto_trap_return(kstack_ptr: usize) -> Self {
Self {
ra: trap_return as usize,
sp: kstack_ptr,
s: [0; 12],
}
}

View file

@ -1,7 +1,7 @@
use crate::sync::UPSafeCell;
use super::TaskControlBlock;
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use spin::Mutex;
use lazy_static::*;
pub struct TaskManager {
@ -22,13 +22,15 @@ impl TaskManager {
}
lazy_static! {
pub static ref TASK_MANAGER: Mutex<TaskManager> = Mutex::new(TaskManager::new());
pub static ref TASK_MANAGER: UPSafeCell<TaskManager> = unsafe {
UPSafeCell::new(TaskManager::new())
};
}
pub fn add_task(task: Arc<TaskControlBlock>) {
TASK_MANAGER.lock().add(task);
TASK_MANAGER.exclusive_access().add(task);
}
pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
TASK_MANAGER.lock().fetch()
TASK_MANAGER.exclusive_access().fetch()
}

View file

@ -5,7 +5,7 @@ mod manager;
mod processor;
mod pid;
use crate::loader::{get_app_data_by_name};
use crate::loader::get_app_data_by_name;
use switch::__switch;
use task::{TaskControlBlock, TaskStatus};
use alloc::sync::Arc;
@ -28,51 +28,51 @@ pub fn suspend_current_and_run_next() {
// There must be an application running.
let task = take_current_task().unwrap();
// ---- hold current PCB lock
let mut task_inner = task.acquire_inner_lock();
let task_cx_ptr2 = task_inner.get_task_cx_ptr2();
// ---- access current TCB exclusively
let mut task_inner = task.inner_exclusive_access();
let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
// Change status to Ready
task_inner.task_status = TaskStatus::Ready;
drop(task_inner);
// ---- release current PCB lock
// ---- release current PCB
// push back to ready queue.
add_task(task);
// jump to scheduling cycle
schedule(task_cx_ptr2);
schedule(task_cx_ptr);
}
pub fn exit_current_and_run_next(exit_code: i32) {
// take from Processor
let task = take_current_task().unwrap();
// **** hold current PCB lock
let mut inner = task.acquire_inner_lock();
// **** access current TCB exclusively
let mut inner = task.inner_exclusive_access();
// Change status to Zombie
inner.task_status = TaskStatus::Zombie;
// Record exit code
inner.exit_code = exit_code;
// do not move to its parent but under initproc
// ++++++ hold initproc PCB lock here
// ++++++ access initproc TCB exclusively
{
let mut initproc_inner = INITPROC.acquire_inner_lock();
let mut initproc_inner = INITPROC.inner_exclusive_access();
for child in inner.children.iter() {
child.acquire_inner_lock().parent = Some(Arc::downgrade(&INITPROC));
child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
initproc_inner.children.push(child.clone());
}
}
// ++++++ release parent PCB lock here
// ++++++ release parent PCB
inner.children.clear();
// deallocate user space
inner.memory_set.recycle_data_pages();
drop(inner);
// **** release current PCB lock
// **** release current PCB
// drop task manually to maintain rc correctly
drop(task);
// we do not have to save task context
let _unused: usize = 0;
schedule(&_unused as *const _);
let mut _unused = TaskContext::zero_init();
schedule(&mut _unused as *mut _);
}
lazy_static! {

View file

@ -1,6 +1,6 @@
use alloc::vec::Vec;
use lazy_static::*;
use spin::Mutex;
use crate::sync::UPSafeCell;
use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr};
use crate::config::{
PAGE_SIZE,
@ -39,7 +39,9 @@ impl PidAllocator {
}
lazy_static! {
static ref PID_ALLOCATOR : Mutex<PidAllocator> = Mutex::new(PidAllocator::new());
static ref PID_ALLOCATOR : UPSafeCell<PidAllocator> = unsafe {
UPSafeCell::new(PidAllocator::new())
};
}
pub struct PidHandle(pub usize);
@ -47,12 +49,12 @@ pub struct PidHandle(pub usize);
impl Drop for PidHandle {
fn drop(&mut self) {
//println!("drop pid {}", self.0);
PID_ALLOCATOR.lock().dealloc(self.0);
PID_ALLOCATOR.exclusive_access().dealloc(self.0);
}
}
pub fn pid_alloc() -> PidHandle {
PID_ALLOCATOR.lock().alloc()
PID_ALLOCATOR.exclusive_access().alloc()
}
/// Return (bottom, top) of a kernel stack in kernel space.
@ -71,7 +73,7 @@ impl KernelStack {
let pid = pid_handle.0;
let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid);
KERNEL_SPACE
.lock()
.exclusive_access()
.insert_framed_area(
kernel_stack_bottom.into(),
kernel_stack_top.into(),
@ -81,6 +83,7 @@ impl KernelStack {
pid: pid_handle.0,
}
}
#[allow(unused)]
pub fn push_on_top<T>(&self, value: T) -> *mut T where
T: Sized, {
let kernel_stack_top = self.get_top();
@ -99,7 +102,7 @@ impl Drop for KernelStack {
let (kernel_stack_bottom, _) = kernel_stack_position(self.pid);
let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
KERNEL_SPACE
.lock()
.exclusive_access()
.remove_area_with_start_vpn(kernel_stack_bottom_va.into());
}
}

View file

@ -1,95 +1,90 @@
use super::TaskControlBlock;
use super::{TaskContext, TaskControlBlock};
use alloc::sync::Arc;
use core::cell::RefCell;
use lazy_static::*;
use super::{fetch_task, TaskStatus};
use super::__switch;
use crate::trap::TrapContext;
use crate::sync::UPSafeCell;
pub struct Processor {
inner: RefCell<ProcessorInner>,
}
unsafe impl Sync for Processor {}
struct ProcessorInner {
current: Option<Arc<TaskControlBlock>>,
idle_task_cx_ptr: usize,
idle_task_cx: TaskContext,
}
impl Processor {
pub fn new() -> Self {
Self {
inner: RefCell::new(ProcessorInner {
current: None,
idle_task_cx_ptr: 0,
}),
current: None,
idle_task_cx: TaskContext::zero_init(),
}
}
fn get_idle_task_cx_ptr2(&self) -> *const usize {
let inner = self.inner.borrow();
&inner.idle_task_cx_ptr as *const usize
fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext {
&mut self.idle_task_cx as *mut _
}
pub fn run(&self) {
loop {
if let Some(task) = fetch_task() {
let idle_task_cx_ptr2 = self.get_idle_task_cx_ptr2();
// acquire
let mut task_inner = task.acquire_inner_lock();
let next_task_cx_ptr2 = task_inner.get_task_cx_ptr2();
task_inner.task_status = TaskStatus::Running;
drop(task_inner);
// release
self.inner.borrow_mut().current = Some(task);
unsafe {
__switch(
idle_task_cx_ptr2,
next_task_cx_ptr2,
);
}
}
}
}
pub fn take_current(&self) -> Option<Arc<TaskControlBlock>> {
self.inner.borrow_mut().current.take()
pub fn take_current(&mut self) -> Option<Arc<TaskControlBlock>> {
self.current.take()
}
pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
self.inner.borrow().current.as_ref().map(|task| Arc::clone(task))
self.current.as_ref().map(|task| Arc::clone(task))
}
}
lazy_static! {
pub static ref PROCESSOR: Processor = Processor::new();
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe {
UPSafeCell::new(Processor::new())
};
}
pub fn run_tasks() {
PROCESSOR.run();
loop {
let mut processor = PROCESSOR.exclusive_access();
if let Some(task) = fetch_task() {
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
// access coming task TCB exclusively
let mut task_inner = task.inner_exclusive_access();
let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
task_inner.task_status = TaskStatus::Running;
drop(task_inner);
// release coming task TCB manually
processor.current = Some(task);
// release processor manually
drop(processor);
unsafe {
__switch(
idle_task_cx_ptr,
next_task_cx_ptr,
);
}
}
}
}
pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.take_current()
PROCESSOR.exclusive_access().take_current()
}
pub fn current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.current()
PROCESSOR.exclusive_access().current()
}
pub fn current_user_token() -> usize {
let task = current_task().unwrap();
let token = task.acquire_inner_lock().get_user_token();
let token = task.inner_exclusive_access().get_user_token();
token
}
pub fn current_trap_cx() -> &'static mut TrapContext {
current_task().unwrap().acquire_inner_lock().get_trap_cx()
current_task().unwrap().inner_exclusive_access().get_trap_cx()
}
pub fn schedule(switched_task_cx_ptr2: *const usize) {
let idle_task_cx_ptr2 = PROCESSOR.get_idle_task_cx_ptr2();
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
let mut processor = PROCESSOR.exclusive_access();
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
drop(processor);
unsafe {
__switch(
switched_task_cx_ptr2,
idle_task_cx_ptr2,
switched_task_cx_ptr,
idle_task_cx_ptr,
);
}
}

View file

@ -1,37 +1,34 @@
.altmacro
.macro SAVE_SN n
sd s\n, (\n+1)*8(sp)
sd s\n, (\n+2)*8(a0)
.endm
.macro LOAD_SN n
ld s\n, (\n+1)*8(sp)
ld s\n, (\n+2)*8(a1)
.endm
.section .text
.globl __switch
__switch:
# __switch(
# current_task_cx_ptr2: &*const TaskContext,
# next_task_cx_ptr2: &*const TaskContext
# current_task_cx_ptr: *mut TaskContext,
# next_task_cx_ptr: *const TaskContext
# )
# push TaskContext to current sp and save its address to where a0 points to
addi sp, sp, -13*8
sd sp, 0(a0)
# fill TaskContext with ra & s0-s11
sd ra, 0(sp)
# save kernel stack of current task
sd sp, 8(a0)
# save ra & s0~s11 of current execution
sd ra, 0(a0)
.set n, 0
.rept 12
SAVE_SN %n
.set n, n + 1
.endr
# ready for loading TaskContext a1 points to
ld sp, 0(a1)
# load registers in the TaskContext
ld ra, 0(sp)
# restore ra & s0~s11 of next execution
ld ra, 0(a1)
.set n, 0
.rept 12
LOAD_SN %n
.set n, n + 1
.endr
# pop TaskContext
addi sp, sp, 13*8
# restore kernel stack of next task
ld sp, 8(a1)
ret

View file

@ -1,8 +1,10 @@
global_asm!(include_str!("switch.S"));
use super::TaskContext;
extern "C" {
pub fn __switch(
current_task_cx_ptr2: *const usize,
next_task_cx_ptr2: *const usize
current_task_cx_ptr: *mut TaskContext,
next_task_cx_ptr: *const TaskContext
);
}

View file

@ -1,12 +1,13 @@
use crate::mm::{MemorySet, PhysPageNum, KERNEL_SPACE, VirtAddr};
use crate::trap::{TrapContext, trap_handler};
use crate::config::{TRAP_CONTEXT};
use crate::config::TRAP_CONTEXT;
use crate::sync::UPSafeCell;
use core::cell::RefMut;
use super::TaskContext;
use super::{PidHandle, pid_alloc, KernelStack};
use alloc::sync::{Weak, Arc};
use alloc::vec;
use alloc::vec::Vec;
use spin::{Mutex, MutexGuard};
use crate::fs::{File, Stdin, Stdout};
pub struct TaskControlBlock {
@ -14,13 +15,13 @@ pub struct TaskControlBlock {
pub pid: PidHandle,
pub kernel_stack: KernelStack,
// mutable
inner: Mutex<TaskControlBlockInner>,
inner: UPSafeCell<TaskControlBlockInner>,
}
pub struct TaskControlBlockInner {
pub trap_cx_ppn: PhysPageNum,
pub base_size: usize,
pub task_cx_ptr: usize,
pub task_cx: TaskContext,
pub task_status: TaskStatus,
pub memory_set: MemorySet,
pub parent: Option<Weak<TaskControlBlock>>,
@ -30,9 +31,6 @@ pub struct TaskControlBlockInner {
}
impl TaskControlBlockInner {
pub fn get_task_cx_ptr2(&self) -> *const usize {
&self.task_cx_ptr as *const usize
}
pub fn get_trap_cx(&self) -> &'static mut TrapContext {
self.trap_cx_ppn.get_mut()
}
@ -57,8 +55,8 @@ impl TaskControlBlockInner {
}
impl TaskControlBlock {
pub fn acquire_inner_lock(&self) -> MutexGuard<TaskControlBlockInner> {
self.inner.lock()
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
self.inner.exclusive_access()
}
pub fn new(elf_data: &[u8]) -> Self {
// memory_set with elf program headers/trampoline/trap context/user stack
@ -71,15 +69,13 @@ impl TaskControlBlock {
let pid_handle = pid_alloc();
let kernel_stack = KernelStack::new(&pid_handle);
let kernel_stack_top = kernel_stack.get_top();
// push a task context which goes to trap_return to the top of kernel stack
let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
let task_control_block = Self {
pid: pid_handle,
kernel_stack,
inner: Mutex::new(TaskControlBlockInner {
inner: unsafe { UPSafeCell::new(TaskControlBlockInner {
trap_cx_ppn,
base_size: user_sp,
task_cx_ptr: task_cx_ptr as usize,
task_cx: TaskContext::goto_trap_return(kernel_stack_top),
task_status: TaskStatus::Ready,
memory_set,
parent: None,
@ -93,14 +89,14 @@ impl TaskControlBlock {
// 2 -> stderr
Some(Arc::new(Stdout)),
],
}),
})},
};
// prepare TrapContext in user space
let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx();
*trap_cx = TrapContext::app_init_context(
entry_point,
user_sp,
KERNEL_SPACE.lock().token(),
KERNEL_SPACE.exclusive_access().token(),
kernel_stack_top,
trap_handler as usize,
);
@ -114,8 +110,8 @@ impl TaskControlBlock {
.unwrap()
.ppn();
// **** hold current PCB lock
let mut inner = self.acquire_inner_lock();
// **** access inner exclusively
let mut inner = self.inner_exclusive_access();
// substitute memory_set
inner.memory_set = memory_set;
// update trap_cx ppn
@ -125,15 +121,15 @@ impl TaskControlBlock {
*trap_cx = TrapContext::app_init_context(
entry_point,
user_sp,
KERNEL_SPACE.lock().token(),
KERNEL_SPACE.exclusive_access().token(),
self.kernel_stack.get_top(),
trap_handler as usize,
);
// **** release current PCB lock
// **** release inner automatically
}
pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
// ---- hold parent PCB lock
let mut parent_inner = self.acquire_inner_lock();
// ---- access parent PCB exclusively
let mut parent_inner = self.inner_exclusive_access();
// copy user space(include trap context)
let memory_set = MemorySet::from_existed_user(
&parent_inner.memory_set
@ -146,8 +142,6 @@ impl TaskControlBlock {
let pid_handle = pid_alloc();
let kernel_stack = KernelStack::new(&pid_handle);
let kernel_stack_top = kernel_stack.get_top();
// push a goto_trap_return task_cx on the top of kernel stack
let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
// copy fd table
let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
for fd in parent_inner.fd_table.iter() {
@ -160,28 +154,28 @@ impl TaskControlBlock {
let task_control_block = Arc::new(TaskControlBlock {
pid: pid_handle,
kernel_stack,
inner: Mutex::new(TaskControlBlockInner {
inner: unsafe { UPSafeCell::new(TaskControlBlockInner {
trap_cx_ppn,
base_size: parent_inner.base_size,
task_cx_ptr: task_cx_ptr as usize,
task_cx: TaskContext::goto_trap_return(kernel_stack_top),
task_status: TaskStatus::Ready,
memory_set,
parent: Some(Arc::downgrade(self)),
children: Vec::new(),
exit_code: 0,
fd_table: new_fd_table,
}),
})},
});
// add child
parent_inner.children.push(task_control_block.clone());
// modify kernel_sp in trap_cx
// **** acquire child PCB lock
let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
// **** release child PCB lock
// **** access child PCB exclusively
let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx();
trap_cx.kernel_sp = kernel_stack_top;
// return
task_control_block
// ---- release parent PCB lock
// **** release child PCB
// ---- release parent PCB
}
pub fn getpid(&self) -> usize {
self.pid.0