From 3f402d2d88d279e8fee8e4ea9c798b795fd338fb Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Tue, 12 Dec 2023 07:00:40 +0000 Subject: [PATCH] Add syscall alarm --- framework/aster-frame/src/timer.rs | 2 +- .../src/process/posix_thread/builder.rs | 27 ++++- .../aster-nix/src/process/posix_thread/mod.rs | 27 +++-- .../src/process/posix_thread/timer.rs | 111 ++++++++++++++++++ kernel/aster-nix/src/syscall/alarm.rs | 29 +++++ kernel/aster-nix/src/syscall/mod.rs | 22 ++-- regression/syscall_test/Makefile | 2 +- regression/syscall_test/blocklists/alarm_test | 2 + 8 files changed, 199 insertions(+), 23 deletions(-) create mode 100644 kernel/aster-nix/src/process/posix_thread/timer.rs create mode 100644 kernel/aster-nix/src/syscall/alarm.rs create mode 100644 regression/syscall_test/blocklists/alarm_test diff --git a/framework/aster-frame/src/timer.rs b/framework/aster-frame/src/timer.rs index dd7a45873..0e5d7edae 100644 --- a/framework/aster-frame/src/timer.rs +++ b/framework/aster-frame/src/timer.rs @@ -58,7 +58,7 @@ impl Timer { /// /// If a timeout value is already set, the timeout value will be refreshed. /// - pub fn set(self: Arc, timeout: Duration) { + pub fn set(self: &Arc, timeout: Duration) { let mut lock = self.inner.lock_irq_disabled(); match &lock.timer_callback { Some(callback) => { diff --git a/kernel/aster-nix/src/process/posix_thread/builder.rs b/kernel/aster-nix/src/process/posix_thread/builder.rs index 0ca024736..558e02475 100644 --- a/kernel/aster-nix/src/process/posix_thread/builder.rs +++ b/kernel/aster-nix/src/process/posix_thread/builder.rs @@ -2,15 +2,18 @@ use aster_frame::user::UserSpace; -use super::PosixThread; +use super::{PosixThread, PosixThreadExt, RealTimer}; use crate::{ prelude::*, process::{ posix_thread::name::ThreadName, - signal::{sig_mask::SigMask, sig_queues::SigQueues}, + signal::{ + constants::SIGALRM, sig_mask::SigMask, sig_queues::SigQueues, + signals::kernel::KernelSignal, + }, Credentials, Process, }, - thread::{status::ThreadStatus, task::create_new_user_task, thread_table, Thread, Tid}, + thread::{status::ThreadStatus, task, thread_table, Thread, Tid}, }; /// The builder to build a posix thread @@ -90,8 +93,23 @@ impl PosixThreadBuilder { sig_queues, is_main_thread, } = self; + + let real_timer = RealTimer::new(move || { + let process = { + let Some(current_thread) = thread_table::get_thread(tid) else { + return; + }; + let posix_thread = current_thread.as_posix_thread().unwrap(); + posix_thread.process() + }; + + let signal = KernelSignal::new(SIGALRM); + process.enqueue_signal(signal); + }) + .unwrap(); + let thread = Arc::new_cyclic(|thread_ref| { - let task = create_new_user_task(user_space, thread_ref.clone()); + let task = task::create_new_user_task(user_space, thread_ref.clone()); let status = ThreadStatus::Init; let posix_thread = PosixThread { process, @@ -100,6 +118,7 @@ impl PosixThreadBuilder { set_child_tid: Mutex::new(set_child_tid), clear_child_tid: Mutex::new(clear_child_tid), credentials, + real_timer: Mutex::new(real_timer), sig_mask: Mutex::new(sig_mask), sig_queues: Mutex::new(sig_queues), sig_context: Mutex::new(None), diff --git a/kernel/aster-nix/src/process/posix_thread/mod.rs b/kernel/aster-nix/src/process/posix_thread/mod.rs index 1cf9a0225..0fbd7eaf6 100644 --- a/kernel/aster-nix/src/process/posix_thread/mod.rs +++ b/kernel/aster-nix/src/process/posix_thread/mod.rs @@ -26,11 +26,13 @@ pub mod futex; mod name; mod posix_thread_ext; mod robust_list; +mod timer; pub use builder::PosixThreadBuilder; pub use name::{ThreadName, MAX_THREAD_NAME_LEN}; pub use posix_thread_ext::PosixThreadExt; pub use robust_list::RobustListHead; +pub use timer::RealTimer; pub struct PosixThread { // Immutable part @@ -50,10 +52,13 @@ pub struct PosixThread { /// Process credentials. At the kernel level, credentials are a per-thread attribute. credentials: Credentials, - // signal - /// blocked signals + /// The timer counts down in real (i.e., wall clock) time + real_timer: Mutex, + + // Signal + /// Blocked signals sig_mask: Mutex, - /// thread-directed sigqueue + /// Thread-directed sigqueue sig_queues: Mutex, /// Signal handler ucontext address /// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp. @@ -114,12 +119,12 @@ impl PosixThread { let receiver_sid = self.process().session().unwrap().sid(); if receiver_sid == sender.sid() { return Ok(()); - } else { - return_errno_with_message!( - Errno::EPERM, - "sigcont requires that sender and receiver belongs to the same session" - ); } + + return_errno_with_message!( + Errno::EPERM, + "sigcont requires that sender and receiver belongs to the same session" + ); } let (receiver_ruid, receiver_suid) = { @@ -141,6 +146,10 @@ impl PosixThread { return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed."); } + pub fn real_timer(&self) -> &Mutex { + &self.real_timer + } + pub(in crate::process) fn enqueue_signal(&self, signal: Box) { self.sig_queues.lock().enqueue(signal); } @@ -233,6 +242,8 @@ impl PosixThread { thread_table::remove_thread(tid); } + self.real_timer.lock().clear(); + if self.is_main_thread() || self.is_last_thread() { // exit current process. debug!("self is main thread or last thread"); diff --git a/kernel/aster-nix/src/process/posix_thread/timer.rs b/kernel/aster-nix/src/process/posix_thread/timer.rs new file mode 100644 index 000000000..324c1d122 --- /dev/null +++ b/kernel/aster-nix/src/process/posix_thread/timer.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::time::Duration; + +use crate::{ + prelude::*, + thread::work_queue::{submit_work_item, work_item::WorkItem, WorkPriority}, + time::SystemTime, +}; + +/// A timer that counts down in real (wall clock) time to run delayed callbacks in process context. +/// +/// Unlike the `Timer` in `aster-frame`, the callbacks of this `RealTimer` will be executed in process +/// context instead of interrupt context. This leads to two differences: +/// +/// 1. The callbacks of this `RealTimer` can sleep, whereas the callbacks of `Timer` in `aster-frame` cannot. +/// 2. The callbacks of this `RealTimer` may be delayed by an arbitrary amount of time due to scheduler strategy. +/// +/// Note that the callbacks may not be executed in the process context of the timer's creator, so macros such +/// as `current` and `current_thread` should **NOT** be used in the callback. +/// +/// # Example +/// ```rust +/// let current_tid = current_thread!().tid(); +/// let timer = RealTimer::new(move || { +/// let current_thread = thread_table::get_thread(current_tid); +/// let posix_thread = current_thread.as_posix_thread().unwrap(); +/// let process = posix_thread.process(); +/// println!("Task executed for PID: {}", process.pid()); +/// }, Duration::from_secs(1)); +pub struct RealTimer { + timer: Arc, + expired_time: Option, +} + +impl RealTimer { + /// Creates a new `RealTimer`. The `callback` parameter will be called once the timeout is reached. + pub fn new(callback: impl Fn() + Send + Sync + Copy + 'static) -> Result { + let timer = { + aster_frame::timer::Timer::new(move |timer| { + let work_func = Box::new(callback); + let work_item = { Arc::new(WorkItem::new(work_func)) }; + // FIXME: set a higher priority like `WorkPriority::Alarm`. + submit_work_item(work_item, WorkPriority::High); + })? + }; + + Ok(Self { + timer, + expired_time: None, + }) + } + + /// Sets a new timeout value. If the old timeout is already set, the timeout will be refreshed. + pub fn set(&mut self, timeout: Duration) -> Result<()> { + assert_ne!(timeout, ZERO_DURATION); + + let new_expired_time = { + let now = SystemTime::now(); + now.checked_add(timeout) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "Invalid duration"))? + }; + + self.expired_time = Some(new_expired_time); + + self.timer.set(timeout); + + Ok(()) + } + + /// Returns the remaining time until the task is executed. If the `timer` is expired or cleared, + /// this method will return zero. + pub fn remain(&self) -> Duration { + let Some(expired_time) = &self.expired_time else { + return ZERO_DURATION; + }; + + let now = SystemTime::now(); + match expired_time.duration_since(&now) { + Ok(duration) => duration, + Err(_) => ZERO_DURATION, + } + } + + /// Returns whether the timer has expired. + pub fn is_expired(&self) -> bool { + self.remain() == ZERO_DURATION + } + + /// Clears the timer. + pub fn clear(&mut self) { + self.timer.clear(); + self.expired_time = None; + } +} + +impl Drop for RealTimer { + fn drop(&mut self) { + self.timer.clear(); + } +} + +impl PartialEq for RealTimer { + fn eq(&self, other: &Self) -> bool { + self.expired_time == other.expired_time + } +} + +impl Eq for RealTimer {} + +const ZERO_DURATION: Duration = Duration::new(0, 0); diff --git a/kernel/aster-nix/src/syscall/alarm.rs b/kernel/aster-nix/src/syscall/alarm.rs new file mode 100644 index 000000000..88df75b92 --- /dev/null +++ b/kernel/aster-nix/src/syscall/alarm.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::time::Duration; + +use super::{SyscallReturn, SYS_ALARM}; +use crate::{log_syscall_entry, prelude::*, process::posix_thread::PosixThreadExt}; + +pub fn sys_alarm(seconds: u32) -> Result { + log_syscall_entry!(SYS_ALARM); + debug!("seconds = {}", seconds); + + let current_thread = current_thread!(); + let mut real_timer = { + let posix_thread = current_thread.as_posix_thread().unwrap(); + posix_thread.real_timer().lock() + }; + + let remaining_secs = real_timer.remain().as_secs(); + + if seconds == 0 { + // Clear previous timer + real_timer.clear(); + return Ok(SyscallReturn::Return(remaining_secs as _)); + } + + real_timer.set(Duration::from_secs(seconds as u64))?; + + Ok(SyscallReturn::Return(remaining_secs as _)) +} diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index 16052d7e0..5285f5c25 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -5,15 +5,16 @@ use aster_frame::cpu::UserContext; use self::{ - accept::sys_accept, bind::sys_bind, connect::sys_connect, execve::sys_execveat, - getgroups::sys_getgroups, getpeername::sys_getpeername, getrandom::sys_getrandom, - getresgid::sys_getresgid, getresuid::sys_getresuid, getsid::sys_getsid, - getsockname::sys_getsockname, getsockopt::sys_getsockopt, listen::sys_listen, - pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto, setfsgid::sys_setfsgid, - setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, setregid::sys_setregid, - setresgid::sys_setresgid, setresuid::sys_setresuid, setreuid::sys_setreuid, setsid::sys_setsid, - setsockopt::sys_setsockopt, setuid::sys_setuid, shutdown::sys_shutdown, - sigaltstack::sys_sigaltstack, socket::sys_socket, socketpair::sys_socketpair, + accept::sys_accept, alarm::sys_alarm, bind::sys_bind, connect::sys_connect, + execve::sys_execveat, getgroups::sys_getgroups, getpeername::sys_getpeername, + getrandom::sys_getrandom, getresgid::sys_getresgid, getresuid::sys_getresuid, + getsid::sys_getsid, getsockname::sys_getsockname, getsockopt::sys_getsockopt, + listen::sys_listen, pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto, + setfsgid::sys_setfsgid, setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, + setregid::sys_setregid, setresgid::sys_setresgid, setresuid::sys_setresuid, + setreuid::sys_setreuid, setsid::sys_setsid, setsockopt::sys_setsockopt, setuid::sys_setuid, + shutdown::sys_shutdown, sigaltstack::sys_sigaltstack, socket::sys_socket, + socketpair::sys_socketpair, }; use crate::{ prelude::*, @@ -96,6 +97,7 @@ use crate::{ mod accept; mod access; +mod alarm; mod arch_prctl; mod bind; mod brk; @@ -257,6 +259,7 @@ define_syscall_nums!( SYS_DUP = 32, SYS_DUP2 = 33, SYS_PAUSE = 34, + SYS_ALARM = 37, SYS_GETPID = 39, SYS_SOCKET = 41, SYS_CONNECT = 42, @@ -443,6 +446,7 @@ pub fn syscall_dispatch( SYS_DUP => syscall_handler!(1, sys_dup, args), SYS_DUP2 => syscall_handler!(2, sys_dup2, args), SYS_PAUSE => syscall_handler!(0, sys_pause), + SYS_ALARM => syscall_handler!(1, sys_alarm, args), SYS_GETPID => syscall_handler!(0, sys_getpid), SYS_SOCKET => syscall_handler!(3, sys_socket, args), SYS_CONNECT => syscall_handler!(3, sys_connect, args), diff --git a/regression/syscall_test/Makefile b/regression/syscall_test/Makefile index e50acf1cd..1232158c2 100644 --- a/regression/syscall_test/Makefile +++ b/regression/syscall_test/Makefile @@ -3,7 +3,7 @@ TESTS ?= chmod_test chown_test fsync_test getdents_test link_test lseek_test mkdir_test \ open_create_test open_test pty_test read_test rename_test stat_test \ statfs_test symlink_test sync_test truncate_test uidgid_test unlink_test \ - vdso_clock_gettime_test write_test + vdso_clock_gettime_test write_test alarm_test MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) diff --git a/regression/syscall_test/blocklists/alarm_test b/regression/syscall_test/blocklists/alarm_test new file mode 100644 index 000000000..61deb4c35 --- /dev/null +++ b/regression/syscall_test/blocklists/alarm_test @@ -0,0 +1,2 @@ +AlarmTest.UserModeSpinning +AlarmTest.Restart_NoRandomSave