Add itimer-related syscalls

This commit is contained in:
Chen Chengjun
2024-05-31 18:24:01 +08:00
committed by Tate, Hongliang Tian
parent 13fd173b24
commit c5ec2e181e
7 changed files with 432 additions and 2 deletions

View File

@ -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::<i32>() * 2 + size_of::<sigval_t>();
const SIGEV_PAD_SIZE: usize = (SIGEV_MAX_SIZE - SIGEV_PREAMBLE_SIZE) / size_of::<i32>();
#[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,
}

View File

@ -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]);

View File

@ -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;

View File

@ -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<SyscallReturn> {
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::<itimerval_t>(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<SyscallReturn> {
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))
}

View File

@ -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<SyscallReturn> {
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<dyn Fn() + Send + Sync + 'static> = {
// 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_t>(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<SyscallReturn> {
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))
}

View File

@ -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<SyscallReturn> {
if new_itimerspec_addr == 0 {
return_errno_with_message!(Errno::EINVAL, "invalid pointer to new value");
}
let new_itimerspec = read_val_from_user::<itimerspec_t>(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<SyscallReturn> {
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))
}

View File

@ -95,3 +95,19 @@ impl From<UnixTime> 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,
}