diff --git a/book/src/kernel/linux-compatibility/README.md b/book/src/kernel/linux-compatibility/README.md index 4f91ba5bb..096480db4 100644 --- a/book/src/kernel/linux-compatibility/README.md +++ b/book/src/kernel/linux-compatibility/README.md @@ -148,7 +148,7 @@ which are summarized in the table below. | 125 | capget | ✅ | [⚠️](syscall-flag-coverage/namespaces-cgroups-and-security/#capget-and-capset) | | 126 | capset | ✅ | [⚠️](syscall-flag-coverage/namespaces-cgroups-and-security/#capget-and-capset) | | 127 | rt_sigpending | ✅ | 💯 | -| 128 | rt_sigtimedwait | ❌ | N/A | +| 128 | rt_sigtimedwait | ✅ | 💯 | | 129 | rt_sigqueueinfo | ❌ | N/A | | 130 | rt_sigsuspend | ✅ | 💯 | | 131 | sigaltstack | ✅ | ❓ | diff --git a/book/src/kernel/linux-compatibility/syscall-flag-coverage/signals-and-timers/fully_covered.scml b/book/src/kernel/linux-compatibility/syscall-flag-coverage/signals-and-timers/fully_covered.scml index 0cd1bfdc6..4fabe41e3 100644 --- a/book/src/kernel/linux-compatibility/syscall-flag-coverage/signals-and-timers/fully_covered.scml +++ b/book/src/kernel/linux-compatibility/syscall-flag-coverage/signals-and-timers/fully_covered.scml @@ -33,3 +33,6 @@ gettimeofday(tv, tz); // Get time in seconds time(tloc); + +// Synchronously wait for a signal with a timeout +rt_sigtimedwait(set, info, timeout, sigsetsize); diff --git a/kernel/src/syscall/arch/loongarch.rs b/kernel/src/syscall/arch/loongarch.rs index a4373d719..f196712d2 100644 --- a/kernel/src/syscall/arch/loongarch.rs +++ b/kernel/src/syscall/arch/loongarch.rs @@ -95,6 +95,7 @@ use super::{ rt_sigprocmask::sys_rt_sigprocmask, rt_sigreturn::sys_rt_sigreturn, rt_sigsuspend::sys_rt_sigsuspend, + rt_sigtimedwait::sys_rt_sigtimedwait, sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity}, sched_get_priority_max::sys_sched_get_priority_max, sched_get_priority_min::sys_sched_get_priority_min, @@ -271,6 +272,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_RT_SIGACTION = 134 => sys_rt_sigaction(args[..4]); SYS_RT_SIGPROCMASK = 135 => sys_rt_sigprocmask(args[..4]); SYS_RT_SIGPENDING = 136 => sys_rt_sigpending(args[..2]); + SYS_RT_SIGTIMEDWAIT = 137 => sys_rt_sigtimedwait(args[..4]); SYS_RT_SIGRETURN = 139 => sys_rt_sigreturn(args[..0], &mut user_ctx); SYS_SET_PRIORITY = 140 => sys_set_priority(args[..3]); SYS_GET_PRIORITY = 141 => sys_get_priority(args[..2]); diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs index 203985f01..eb494d5a1 100644 --- a/kernel/src/syscall/arch/riscv.rs +++ b/kernel/src/syscall/arch/riscv.rs @@ -95,6 +95,7 @@ use super::{ rt_sigprocmask::sys_rt_sigprocmask, rt_sigreturn::sys_rt_sigreturn, rt_sigsuspend::sys_rt_sigsuspend, + rt_sigtimedwait::sys_rt_sigtimedwait, sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity}, sched_get_priority_max::sys_sched_get_priority_max, sched_get_priority_min::sys_sched_get_priority_min, @@ -271,6 +272,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_RT_SIGACTION = 134 => sys_rt_sigaction(args[..4]); SYS_RT_SIGPROCMASK = 135 => sys_rt_sigprocmask(args[..4]); SYS_RT_SIGPENDING = 136 => sys_rt_sigpending(args[..2]); + SYS_RT_SIGTIMEDWAIT = 137 => sys_rt_sigtimedwait(args[..4]); SYS_RT_SIGRETURN = 139 => sys_rt_sigreturn(args[..0], &mut user_ctx); SYS_SET_PRIORITY = 140 => sys_set_priority(args[..3]); SYS_GET_PRIORITY = 141 => sys_get_priority(args[..2]); diff --git a/kernel/src/syscall/arch/x86.rs b/kernel/src/syscall/arch/x86.rs index 74953a001..d19719391 100644 --- a/kernel/src/syscall/arch/x86.rs +++ b/kernel/src/syscall/arch/x86.rs @@ -105,6 +105,7 @@ use super::{ rt_sigprocmask::sys_rt_sigprocmask, rt_sigreturn::sys_rt_sigreturn, rt_sigsuspend::sys_rt_sigsuspend, + rt_sigtimedwait::sys_rt_sigtimedwait, sched_affinity::{sys_sched_getaffinity, sys_sched_setaffinity}, sched_get_priority_max::sys_sched_get_priority_max, sched_get_priority_min::sys_sched_get_priority_min, @@ -291,6 +292,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CAPGET = 125 => sys_capget(args[..2]); SYS_CAPSET = 126 => sys_capset(args[..2]); SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]); + SYS_RT_SIGTIMEDWAIT = 128 => sys_rt_sigtimedwait(args[..4]); SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]); SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2], &user_ctx); SYS_UTIME = 132 => sys_utime(args[..2]); diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 307c131d1..601d335ad 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -117,6 +117,7 @@ mod rt_sigpending; mod rt_sigprocmask; mod rt_sigreturn; mod rt_sigsuspend; +mod rt_sigtimedwait; mod sched_affinity; mod sched_get_priority_max; mod sched_get_priority_min; diff --git a/kernel/src/syscall/rt_sigtimedwait.rs b/kernel/src/syscall/rt_sigtimedwait.rs new file mode 100644 index 000000000..d04202ebf --- /dev/null +++ b/kernel/src/syscall/rt_sigtimedwait.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::{sync::atomic::Ordering, time::Duration}; + +use ostd::{mm::VmIo, sync::Waiter}; + +use super::SyscallReturn; +use crate::{ + prelude::*, + process::signal::{ + HandlePendingSignal, + constants::{SIGKILL, SIGSTOP}, + sig_mask::{SigMask, SigSet}, + signals::Signal, + with_sigmask_changed, + }, + time::{timespec_t, wait::ManagedTimeout}, +}; + +pub fn sys_rt_sigtimedwait( + set_ptr: Vaddr, + info_ptr: Vaddr, + timeout_ptr: Vaddr, + sigset_size: usize, + ctx: &Context, +) -> Result { + debug!( + "set_ptr = {:#?}, info_ptr = {:#?}, timeout_ptr = {:#?}, sigset_size = {}", + set_ptr, info_ptr, timeout_ptr, sigset_size + ); + + // Validate sigset size + if sigset_size != size_of::() { + return_errno_with_message!(Errno::EINVAL, "invalid sigset size"); + } + + // Read the signal set + let mask = { + let mut set: SigSet = ctx.user_space().read_val(set_ptr)?; + + // Remove SIGKILL and SIGSTOP as they cannot be waited for + set -= SIGKILL; + set -= SIGSTOP; + + !set + }; + + // Read timeout if provided + let timeout = if timeout_ptr != 0 { + let timespec: timespec_t = ctx.user_space().read_val(timeout_ptr)?; + Some(Duration::try_from(timespec)?) + } else { + None + }; + + debug!( + "pid = {}, sig_mask = {:?}, timeout = {:?}", + ctx.process.pid(), + mask, + timeout + ); + + let block_list = ctx.posix_thread.sig_mask().load(Ordering::Relaxed); + // Fast path: If a signal is already pending, dequeue and return it immediately. + if let Some(signal) = dequeue_signal_with_checking_ignore(ctx, mask, block_list) { + if info_ptr != 0 { + let siginfo = signal.to_info(); + ctx.user_space().write_val(info_ptr, &siginfo)?; + } + + return Ok(SyscallReturn::Return(signal.num().as_u8() as _)); + } + + with_sigmask_changed( + ctx, + |sig_mask| sig_mask & mask, + || { + // Wait for a signal to arrive or timeout. + let waiter = Waiter::new_pair().0; + let signal = waiter + .pause_until_or_timeout( + || dequeue_signal_with_checking_ignore(ctx, mask, block_list), + timeout.map(ManagedTimeout::new), + ) + .map_err(|e| { + if e.error() == Errno::ETIME { + Error::new(Errno::EAGAIN) + } else { + e + } + })?; + + if info_ptr != 0 { + let siginfo = signal.to_info(); + ctx.user_space().write_val(info_ptr, &siginfo)?; + } + + Ok(SyscallReturn::Return(signal.num().as_u8() as _)) + }, + ) +} + +/// Dequeue a signal from the thread's pending signal queue. +/// +/// If the signal is ignored and not blocked, it will be dropped and +/// the next signal will be checked. +fn dequeue_signal_with_checking_ignore( + ctx: &Context, + mask: SigMask, + block_list: SigMask, +) -> Option> { + let sig_dispositions = ctx.process.sig_dispositions().lock(); + let sig_disposition = sig_dispositions.lock(); + while let Some(signal) = ctx.posix_thread.dequeue_signal(&mask) { + // If the signal is ignored and not blocked, the signal will be directly dropped. + if !block_list.contains(signal.num()) && sig_disposition.will_ignore(signal.as_ref()) { + continue; + } + + return Some(signal); + } + + None +}