From c5ec2e181ede06a8b347d8190c2f432239a44311 Mon Sep 17 00:00:00 2001 From: Chen Chengjun Date: Fri, 31 May 2024 18:24:01 +0800 Subject: [PATCH] Add itimer-related syscalls --- .../aster-nix/src/process/signal/c_types.rs | 64 ++++++- kernel/aster-nix/src/syscall/arch/x86.rs | 9 + kernel/aster-nix/src/syscall/mod.rs | 5 +- kernel/aster-nix/src/syscall/setitimer.rs | 95 ++++++++++ kernel/aster-nix/src/syscall/timer_create.rs | 168 ++++++++++++++++++ kernel/aster-nix/src/syscall/timer_settime.rs | 77 ++++++++ kernel/aster-nix/src/time/mod.rs | 16 ++ 7 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 kernel/aster-nix/src/syscall/setitimer.rs create mode 100644 kernel/aster-nix/src/syscall/timer_create.rs create mode 100644 kernel/aster-nix/src/syscall/timer_settime.rs diff --git a/kernel/aster-nix/src/process/signal/c_types.rs b/kernel/aster-nix/src/process/signal/c_types.rs index 58c0784be..accbebb37 100644 --- a/kernel/aster-nix/src/process/signal/c_types.rs +++ b/kernel/aster-nix/src/process/signal/c_types.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 #![allow(non_camel_case_types)] -use core::mem; +use core::mem::{self, size_of}; use aster_frame::cpu::GeneralRegs; use aster_util::{read_union_fields, union_read_ptr::UnionReadPtr}; @@ -116,6 +116,16 @@ pub union sigval_t { sigval_ptr: Vaddr, //*mut c_void } +impl sigval_t { + pub fn read_int(&self) -> i32 { + read_union_fields!(self.sigval_int) + } + + pub fn read_ptr(&self) -> Vaddr { + read_union_fields!(self.sigval_ptr) + } +} + #[derive(Clone, Copy, Pod)] #[repr(C)] union siginfo_sigchild_t { @@ -198,3 +208,55 @@ pub struct SignalCpuContext { pub fpregs_on_heap: u64, pub fpregs: Vaddr, // *mut FpRegs, } + +#[derive(Clone, Copy, Pod)] +#[repr(C)] +pub struct _sigev_thread { + pub function: Vaddr, + pub attribute: Vaddr, +} + +const SIGEV_MAX_SIZE: usize = 64; +/// The total size of the fields `sigev_value`, `sigev_signo` and `sigev_notify`. +const SIGEV_PREAMBLE_SIZE: usize = size_of::() * 2 + size_of::(); +const SIGEV_PAD_SIZE: usize = (SIGEV_MAX_SIZE - SIGEV_PREAMBLE_SIZE) / size_of::(); + +#[derive(Clone, Copy, Pod)] +#[repr(C)] +pub union _sigev_un { + pub _pad: [i32; SIGEV_PAD_SIZE], + pub _tid: i32, + pub _sigev_thread: _sigev_thread, +} + +impl _sigev_un { + pub fn read_tid(&self) -> i32 { + read_union_fields!(self._tid) + } + + pub fn read_function(&self) -> Vaddr { + read_union_fields!(self._sigev_thread.function) + } + + pub fn read_attribute(&self) -> Vaddr { + read_union_fields!(self._sigev_thread.attribute) + } +} + +#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)] +#[repr(i32)] +pub enum SigNotify { + SIGEV_SIGNAL = 0, + SIGEV_NONE = 1, + SIGEV_THREAD = 2, + SIGEV_THREAD_ID = 4, +} + +#[derive(Clone, Copy, Pod)] +#[repr(C)] +pub struct sigevent_t { + pub sigev_value: sigval_t, + pub sigev_signo: i32, + pub sigev_notify: i32, + pub sigev_un: _sigev_un, +} diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index b11513353..669b83893 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -85,6 +85,7 @@ use crate::syscall::{ setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, + setitimer::{sys_getitimer, sys_setitimer}, setpgid::sys_setpgid, setregid::sys_setregid, setresgid::sys_setresgid, @@ -103,6 +104,8 @@ use crate::syscall::{ sync::sys_sync, tgkill::sys_tgkill, time::sys_time, + timer_create::{sys_timer_create, sys_timer_delete}, + timer_settime::{sys_timer_gettime, sys_timer_settime}, truncate::{sys_ftruncate, sys_truncate}, umask::sys_umask, uname::sys_uname, @@ -143,7 +146,9 @@ impl_syscall_nums_and_dispatch_fn! { SYS_DUP2 = 33 => sys_dup2(args[..2]); SYS_PAUSE = 34 => sys_pause(args[..0]); SYS_NANOSLEEP = 35 => sys_nanosleep(args[..2]); + SYS_GETITIMER = 36 => sys_getitimer(args[..2]); SYS_ALARM = 37 => sys_alarm(args[..1]); + SYS_SETITIMER = 38 => sys_setitimer(args[..3]); SYS_GETPID = 39 => sys_getpid(args[..0]); SYS_SENDFILE = 40 => sys_sendfile(args[..4]); SYS_SOCKET = 41 => sys_socket(args[..3]); @@ -227,6 +232,10 @@ impl_syscall_nums_and_dispatch_fn! { SYS_EPOLL_CREATE = 213 => sys_epoll_create(args[..1]); SYS_GETDENTS64 = 217 => sys_getdents64(args[..3]); SYS_SET_TID_ADDRESS = 218 => sys_set_tid_address(args[..1]); + SYS_TIMER_CREATE = 222 => sys_timer_create(args[..3]); + SYS_TIMER_SETTIME = 223 => sys_timer_settime(args[..4]); + SYS_TIMER_GETTIME = 224 => sys_timer_gettime(args[..2]); + SYS_TIMER_DELETE = 226 => sys_timer_delete(args[..1]); SYS_CLOCK_GETTIME = 228 => sys_clock_gettime(args[..2]); SYS_CLOCK_NANOSLEEP = 230 => sys_clock_nanosleep(args[..4]); SYS_EXIT_GROUP = 231 => sys_exit_group(args[..1]); diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index 33888cd0d..3f28c3853 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -3,7 +3,7 @@ //! Read the Cpu context content then dispatch syscall to corrsponding handler //! The each sub module contains functions that handle real syscall logic. use aster_frame::cpu::UserContext; -pub use clock_gettime::ClockID; +pub use clock_gettime::ClockId; use crate::{cpu::LinuxAbi, prelude::*}; @@ -92,6 +92,7 @@ mod setfsgid; mod setfsuid; mod setgid; mod setgroups; +mod setitimer; mod setpgid; mod setregid; mod setresgid; @@ -110,6 +111,8 @@ mod symlink; mod sync; mod tgkill; mod time; +mod timer_create; +mod timer_settime; mod truncate; mod umask; mod uname; diff --git a/kernel/aster-nix/src/syscall/setitimer.rs b/kernel/aster-nix/src/syscall/setitimer.rs new file mode 100644 index 000000000..2c2351008 --- /dev/null +++ b/kernel/aster-nix/src/syscall/setitimer.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MPL-2.0 + +#![allow(non_camel_case_types)] +use core::time::Duration; + +use super::SyscallReturn; +use crate::{ + prelude::*, + time::{itimerval_t, timer::Timeout, timeval_t}, + util::{read_val_from_user, write_val_to_user}, +}; + +/// `ItimerType` is used to differ the target timer for some timer-related syscalls. +#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)] +#[repr(i32)] +pub(super) enum ItimerType { + ITIMER_REAL = 0, + ITIMER_VIRTUAL = 1, + ITIMER_PROF = 2, +} + +pub fn sys_setitimer( + itimer_type: i32, + new_itimerval_addr: Vaddr, + old_itimerval_addr: Vaddr, +) -> Result { + debug!( + "itimer_type = {}, new_itimerval_addr = 0x{:x}, old_itimerval_addr = 0x{:x}, ", + itimer_type, new_itimerval_addr, old_itimerval_addr + ); + + if new_itimerval_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "invalid pointer to new value"); + } + let current = current!(); + let new_itimerval = read_val_from_user::(new_itimerval_addr)?; + let interval = Duration::from(new_itimerval.it_interval); + let expire_time = Duration::from(new_itimerval.it_value); + + let process_timer_manager = current.timer_manager(); + let timer = match ItimerType::try_from(itimer_type)? { + ItimerType::ITIMER_REAL => process_timer_manager.alarm_timer(), + ItimerType::ITIMER_VIRTUAL => process_timer_manager.virtual_timer(), + ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(), + }; + + if old_itimerval_addr > 0 { + let old_interval = timeval_t::from(timer.interval()); + let remain = timeval_t::from(timer.remain()); + let old_itimerval = itimerval_t { + it_interval: old_interval, + it_value: remain, + }; + write_val_to_user(old_itimerval_addr, &old_itimerval)?; + } + + timer.set_interval(interval); + if expire_time == Duration::ZERO { + // Clear previous timer + timer.cancel(); + } else { + timer.set_timeout(Timeout::After(expire_time)); + } + + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_getitimer(itimer_type: i32, itimerval_addr: Vaddr) -> Result { + debug!( + "itimer_type = {}, itimerval_addr = 0x{:x}", + itimer_type, itimerval_addr + ); + + if itimerval_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "invalid pointer to itimerval"); + } + + let current = current!(); + let process_timer_manager = current.timer_manager(); + let timer = match ItimerType::try_from(itimer_type)? { + ItimerType::ITIMER_REAL => process_timer_manager.alarm_timer(), + ItimerType::ITIMER_VIRTUAL => process_timer_manager.virtual_timer(), + ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(), + }; + + let interval = timeval_t::from(timer.interval()); + let remain = timeval_t::from(timer.remain()); + let itimerval = itimerval_t { + it_interval: interval, + it_value: remain, + }; + write_val_to_user(itimerval_addr, &itimerval)?; + + Ok(SyscallReturn::Return(0)) +} diff --git a/kernel/aster-nix/src/syscall/timer_create.rs b/kernel/aster-nix/src/syscall/timer_create.rs new file mode 100644 index 000000000..bc9bdf0a1 --- /dev/null +++ b/kernel/aster-nix/src/syscall/timer_create.rs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::{ + clock_gettime::{DynamicClockIdInfo, DynamicClockType}, + SyscallReturn, +}; +use crate::{ + prelude::*, + process::{ + posix_thread::PosixThreadExt, + process_table, + signal::{ + c_types::{sigevent_t, SigNotify}, + constants::SIGALRM, + sig_num::SigNum, + signals::kernel::KernelSignal, + }, + }, + syscall::ClockId, + thread::{ + thread_table, + work_queue::{submit_work_item, work_item::WorkItem}, + Thread, + }, + time::{ + clockid_t, + clocks::{BootTimeClock, MonotonicClock, RealTimeClock}, + }, + util::{read_val_from_user, write_val_to_user}, +}; + +pub fn sys_timer_create( + clockid: clockid_t, + sigevent_addr: Vaddr, + timer_id_addr: Vaddr, +) -> Result { + if timer_id_addr == 0 { + return_errno_with_message!( + Errno::EINVAL, + "the address of timer_id_addr should be valid" + ); + } + + let current_process = current!(); + let sent_signal: Box = { + // If `sigevent_addr` is NULL, use the default method (like `sys_alarm`) to send signal. + if sigevent_addr == 0 { + let process = current_process.clone(); + let signal = KernelSignal::new(SIGALRM); + Box::new(move || { + process.enqueue_signal(signal); + }) + // Determine the timeout action through `sigevent`. + } else { + let sig_event = read_val_from_user::(sigevent_addr)?; + let sigev_notify = SigNotify::try_from(sig_event.sigev_notify)?; + let signo = sig_event.sigev_signo; + match sigev_notify { + // Do nothing when the timer is expired. + SigNotify::SIGEV_NONE => Box::new(|| {}), + // Send a signal to the current process when the timer is expired. + SigNotify::SIGEV_SIGNAL => { + let process = current_process.clone(); + let signal = KernelSignal::new(SigNum::try_from(signo as u8)?); + Box::new(move || { + process.enqueue_signal(signal); + }) + } + // Spawn a posix thread to run the `sigev_function`, which is stored in + // `sig_event.sigev_un._sigev_thread`. + // + // TODO: enable this instructions. Currently the system does not provide an API to spawn + // a posix thread to run a specified function. + SigNotify::SIGEV_THREAD => { + unimplemented!() + } + // Send a signal to the specified thread when the timer is expired. + SigNotify::SIGEV_THREAD_ID => { + let tid = sig_event.sigev_un.read_tid() as u32; + let thread = thread_table::get_thread(tid).ok_or_else(|| { + Error::with_message(Errno::EINVAL, "target thread does not exist") + })?; + let posix_thread = thread.as_posix_thread().unwrap(); + if posix_thread.process().pid() != current_process.pid() { + return_errno_with_message!( + Errno::EINVAL, + "target thread should belong to current process" + ); + } + let signal = KernelSignal::new(SigNum::try_from(signo as u8)?); + Box::new(move || { + if let Some(thread) = thread.as_posix_thread() { + thread.enqueue_signal(Box::new(signal)); + } + }) + } + } + } + }; + + let work_func = sent_signal; + let work_item = Arc::new(WorkItem::new(work_func)); + let func = move || { + submit_work_item( + work_item.clone(), + crate::thread::work_queue::WorkPriority::High, + ); + }; + + let process_timer_manager = current_process.timer_manager(); + let timer = if clockid >= 0 { + let clock_id = ClockId::try_from(clockid)?; + match clock_id { + ClockId::CLOCK_PROCESS_CPUTIME_ID => process_timer_manager.create_prof_timer(func), + ClockId::CLOCK_THREAD_CPUTIME_ID => { + let current_thread = Thread::current(); + current_thread + .as_posix_thread() + .unwrap() + .create_prof_timer(func) + } + ClockId::CLOCK_REALTIME => RealTimeClock::timer_manager().create_timer(func), + ClockId::CLOCK_MONOTONIC => MonotonicClock::timer_manager().create_timer(func), + ClockId::CLOCK_BOOTTIME => BootTimeClock::timer_manager().create_timer(func), + _ => return_errno_with_message!(Errno::EINVAL, "invalid clock ID"), + } + } else { + let dynamic_clockid_info = DynamicClockIdInfo::try_from(clockid)?; + match dynamic_clockid_info { + DynamicClockIdInfo::Pid(pid, clock_type) => { + let process = process_table::get_process(pid) + .ok_or_else(|| crate::Error::with_message(Errno::EINVAL, "invalid clock id"))?; + let process_timer_manager = process.timer_manager(); + match clock_type { + DynamicClockType::Profiling => process_timer_manager.create_prof_timer(func), + DynamicClockType::Virtual => process_timer_manager.create_virtual_timer(func), + // TODO: support scheduling clock and fd clock. + _ => unimplemented!(), + } + } + DynamicClockIdInfo::Tid(tid, clock_type) => { + let thread = thread_table::get_thread(tid) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid clock id"))?; + let posix_thread = thread.as_posix_thread().unwrap(); + match clock_type { + DynamicClockType::Profiling => posix_thread.create_prof_timer(func), + DynamicClockType::Virtual => posix_thread.create_virtual_timer(func), + _ => unimplemented!(), + } + } + DynamicClockIdInfo::Fd(_) => unimplemented!(), + } + }; + + let timer_id = process_timer_manager.add_posix_timer(timer); + write_val_to_user(timer_id_addr, &timer_id)?; + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_timer_delete(timer_id: usize) -> Result { + let current_process = current!(); + let Some(timer) = current_process.timer_manager().remove_posix_timer(timer_id) else { + return_errno_with_message!(Errno::EINVAL, "invalid timer ID"); + }; + + timer.cancel(); + Ok(SyscallReturn::Return(0)) +} diff --git a/kernel/aster-nix/src/syscall/timer_settime.rs b/kernel/aster-nix/src/syscall/timer_settime.rs new file mode 100644 index 000000000..9061540ad --- /dev/null +++ b/kernel/aster-nix/src/syscall/timer_settime.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::time::Duration; + +use super::SyscallReturn; +use crate::{ + prelude::*, + time::{itimerspec_t, timer::Timeout, timespec_t, TIMER_ABSTIME}, + util::{read_val_from_user, write_val_to_user}, +}; + +pub fn sys_timer_settime( + timer_id: usize, + flags: i32, + new_itimerspec_addr: Vaddr, + old_itimerspec_addr: Vaddr, +) -> Result { + if new_itimerspec_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "invalid pointer to new value"); + } + + let new_itimerspec = read_val_from_user::(new_itimerspec_addr)?; + let interval = Duration::from(new_itimerspec.it_interval); + let expire_time = Duration::from(new_itimerspec.it_value); + + let current_process = current!(); + let Some(timer) = current_process.timer_manager().find_posix_timer(timer_id) else { + return_errno_with_message!(Errno::EINVAL, "invalid timer ID"); + }; + + if old_itimerspec_addr > 0 { + let old_interval = timespec_t::from(timer.interval()); + let remain = timespec_t::from(timer.remain()); + let old_itimerspec = itimerspec_t { + it_interval: old_interval, + it_value: remain, + }; + write_val_to_user(old_itimerspec_addr, &old_itimerspec)?; + } + + timer.set_interval(interval); + if expire_time == Duration::ZERO { + // Clear previous timer + timer.cancel(); + } else { + let timeout = if flags == 0 { + Timeout::After(expire_time) + } else if flags == TIMER_ABSTIME { + Timeout::When(expire_time) + } else { + unreachable!() + }; + timer.set_timeout(timeout); + } + + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_timer_gettime(timer_id: usize, itimerspec_addr: Vaddr) -> Result { + if itimerspec_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "invalid pointer to return value"); + } + let current_process = current!(); + let Some(timer) = current_process.timer_manager().find_posix_timer(timer_id) else { + return_errno_with_message!(Errno::EINVAL, "invalid timer ID"); + }; + + let interval = timespec_t::from(timer.interval()); + let remain = timespec_t::from(timer.remain()); + let itimerspec = itimerspec_t { + it_interval: interval, + it_value: remain, + }; + write_val_to_user(itimerspec_addr, &itimerspec)?; + + Ok(SyscallReturn::Return(0)) +} diff --git a/kernel/aster-nix/src/time/mod.rs b/kernel/aster-nix/src/time/mod.rs index f10849787..f106f0777 100644 --- a/kernel/aster-nix/src/time/mod.rs +++ b/kernel/aster-nix/src/time/mod.rs @@ -95,3 +95,19 @@ impl From for Duration { Duration::from_secs(time.sec as _) } } + +/// This struct is corresponding to the `itimerval` struct in Linux. +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, Pod)] +pub struct itimerval_t { + pub it_interval: timeval_t, + pub it_value: timeval_t, +} + +/// This struct is corresponding to the `itimerspec` struct in Linux. +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, Pod)] +pub struct itimerspec_t { + pub it_interval: timespec_t, + pub it_value: timespec_t, +}