From 1feec06ad054b13fec143b01fcbc8af335db027e Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 31 Oct 2021 22:37:29 +0200 Subject: [PATCH] feat: add process wait channels --- init/src/main.rs | 8 ++- kernel/src/arch/aarch64/timer.rs | 1 + kernel/src/dev/timer.rs | 7 ++ kernel/src/main.rs | 1 + kernel/src/proc/mod.rs | 8 +++ kernel/src/proc/process.rs | 28 +++++++- kernel/src/proc/wait.rs | 115 +++++++++++++++++++++++++++++++ kernel/src/syscall.rs | 9 +++ 8 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 kernel/src/proc/wait.rs diff --git a/init/src/main.rs b/init/src/main.rs index 21b1a8e..4226c75 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,3 +1,4 @@ +#![feature(asm)] #![no_std] #![no_main] @@ -6,6 +7,11 @@ extern crate libusr; #[no_mangle] fn main() -> i32 { - trace!("Hello from userspace"); + loop { + trace!("Hello from userspace"); + unsafe { + asm!("svc #0", in("x8") 121, in("x0") 1000000000); + } + } 123 } diff --git a/kernel/src/arch/aarch64/timer.rs b/kernel/src/arch/aarch64/timer.rs index bd5a241..d7d4ba6 100644 --- a/kernel/src/arch/aarch64/timer.rs +++ b/kernel/src/arch/aarch64/timer.rs @@ -35,6 +35,7 @@ impl IntSource for GenericTimer { CNTP_TVAL_EL0.set(TIMER_TICK); CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET); use crate::proc; + proc::wait::tick(); proc::switch(); Ok(()) } diff --git a/kernel/src/dev/timer.rs b/kernel/src/dev/timer.rs index 4494fda..a946442 100644 --- a/kernel/src/dev/timer.rs +++ b/kernel/src/dev/timer.rs @@ -1,6 +1,7 @@ //! Timer interface use crate::dev::Device; +use crate::proc::Pid; use core::time::Duration; use error::Errno; @@ -9,3 +10,9 @@ pub trait TimestampSource: Device { /// Reads current timestamp as a [Duration] from system start time fn timestamp(&self) -> Result; } + +/// +pub struct Sleep { + deadline: Duration, + pid: Pid +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 8610548..7564aa2 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -10,6 +10,7 @@ const_panic, panic_info_message, alloc_error_handler, + linked_list_cursors, const_btree_new )] #![no_std] diff --git a/kernel/src/proc/mod.rs b/kernel/src/proc/mod.rs index c5c4769..c998390 100644 --- a/kernel/src/proc/mod.rs +++ b/kernel/src/proc/mod.rs @@ -8,6 +8,9 @@ pub mod elf; pub mod process; pub use process::{Pid, Process, ProcessRef, State as ProcessState}; +#[allow(missing_docs)] +pub mod wait; + pub mod sched; pub use sched::Scheduler; pub(self) use sched::SCHED; @@ -38,6 +41,11 @@ pub fn switch() { SCHED.switch(false); } +/// +pub fn process(id: Pid) -> ProcessRef { + PROCESSES.lock().get(&id).unwrap().clone() +} + /// Global list of all processes in the system pub(self) static PROCESSES: IrqSafeSpinLock> = IrqSafeSpinLock::new(BTreeMap::new()); diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 7021cd9..32fce41 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -44,6 +44,7 @@ struct ProcessInner { space: Option<&'static mut Space>, state: State, id: Pid, + wait_flag: bool, exit: Option, } @@ -161,7 +162,7 @@ impl Process { assert_eq!(src_lock.state, State::Running); src_lock.state = State::Ready; } - assert_eq!(dst_lock.state, State::Ready); + assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting); dst_lock.state = State::Running; } @@ -171,6 +172,30 @@ impl Process { (&mut *src_ctx).switch(&mut *dst_ctx); } + /// + pub fn enter_wait(&self) { + let drop = { + let mut lock = self.inner.lock(); + let drop = lock.state == State::Running; + lock.state = State::Waiting; + SCHED.dequeue(lock.id); + drop + }; + if drop { + SCHED.switch(true); + } + } + + /// + pub fn set_wait_flag(&self, v: bool) { + self.inner.lock().wait_flag = v; + } + + /// + pub fn wait_flag(&self) -> bool { + self.inner.lock().wait_flag + } + /// Returns the process ID pub fn id(&self) -> Pid { self.inner.lock().id @@ -185,6 +210,7 @@ impl Process { id, exit: None, space: None, + wait_flag: false, state: State::Ready, }), }); diff --git a/kernel/src/proc/wait.rs b/kernel/src/proc/wait.rs new file mode 100644 index 0000000..c7cccaf --- /dev/null +++ b/kernel/src/proc/wait.rs @@ -0,0 +1,115 @@ +use crate::arch::machine; +use crate::proc::{self, sched::SCHED, Pid, Process, ProcessState}; +use crate::dev::timer::TimestampSource; +use crate::sync::IrqSafeSpinLock; +use alloc::{vec::Vec, collections::{VecDeque, LinkedList}}; +use core::time::Duration; +use error::Errno; + +pub struct Wait { + queue: IrqSafeSpinLock>, +} + +pub struct Timeout { + pid: Pid, + deadline: Duration +} + +static TICK_LIST: IrqSafeSpinLock> = IrqSafeSpinLock::new(LinkedList::new()); + +pub fn tick() { + let time = machine::local_timer().timestamp().unwrap(); + let mut list = TICK_LIST.lock(); + let mut cursor = list.cursor_front_mut(); + + while let Some(item) = cursor.current() { + if time > item.deadline { + let pid = item.pid; + cursor.remove_current(); + SCHED.enqueue(pid); + } else { + cursor.move_next(); + } + } +} + +pub fn sleep(timeout: Duration) { + // Dummy wait descriptor which will never receive notifications + static SLEEP_NOTIFY: Wait = Wait::new(); + SLEEP_NOTIFY.sleep_on(Some(timeout)); +} + +impl Wait { + pub const fn new() -> Self { + Self { + queue: IrqSafeSpinLock::new(LinkedList::new()) + } + } + + pub fn wakeup_all(&self) { + todo!() + } + + pub fn wakeup_one(&self) { + // No IRQs will arrive now == safe to manipulate tick list + let mut tick_lock = TICK_LIST.lock(); + let pid = self.queue.lock().pop_front(); + if let Some(pid) = pid { + let mut cursor = tick_lock.cursor_front_mut(); + while let Some(item) = cursor.current() { + if pid == item.pid { + cursor.remove_current(); + break; + } else { + cursor.move_next(); + } + } + drop(tick_lock); + + proc::process(pid).set_wait_flag(false); + SCHED.enqueue(pid); + } + } + + pub fn sleep_on(&self, timeout: Option) -> Result<(), Errno> { + let proc = Process::current(); + let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t); + let mut queue_lock = Some(self.queue.lock()); + + queue_lock.as_mut().unwrap().push_back(proc.id()); + proc.set_wait_flag(true); + if let Some(deadline) = deadline { + TICK_LIST.lock().push_back(Timeout { + pid: proc.id(), + deadline + }); + } + + loop { + if !proc.wait_flag() { + return Ok(()); + } + + queue_lock = None; + proc.enter_wait(); + queue_lock = Some(self.queue.lock()); + + if let Some(deadline) = deadline { + if machine::local_timer().timestamp()? > deadline { + let mut cursor = queue_lock.as_mut().unwrap().cursor_front_mut(); + + while let Some(&mut item) = cursor.current() { + if proc.id() == item { + cursor.remove_current(); + break; + } else { + cursor.move_next(); + } + } + + return Err(Errno::TimedOut); + } + } + } + } +} diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index d3b00cf..450d6d6 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -59,6 +59,15 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result { println!(Level::Debug, ""); Ok(args[1]) } + // sys_ex_sleep + 121 => { + use crate::proc::wait; + use core::time::Duration; + + wait::sleep(Duration::from_nanos(args[0] as u64)); + + Ok(0) + } _ => panic!("Undefined system call: {}", num), } }