Add syscall ppoll

This commit is contained in:
jiangjianfeng
2025-04-16 08:44:06 +00:00
committed by Tate, Hongliang Tian
parent 1fe0fef410
commit 8815ca384f
12 changed files with 178 additions and 92 deletions

View File

@ -291,7 +291,7 @@ provided by Linux on x86-64 architecture.
| 268 | fchmodat | ✅ | | 268 | fchmodat | ✅ |
| 269 | faccessat | ✅ | | 269 | faccessat | ✅ |
| 270 | pselect6 | ✅ | | 270 | pselect6 | ✅ |
| 271 | ppoll | | | 271 | ppoll | |
| 272 | unshare | ❌ | | 272 | unshare | ❌ |
| 273 | set_robust_list | ✅ | | 273 | set_robust_list | ✅ |
| 274 | get_robust_list | ❌ | | 274 | get_robust_list | ❌ |

View File

@ -20,7 +20,7 @@ use c_types::{siginfo_t, ucontext_t};
use constants::SIGSEGV; use constants::SIGSEGV;
pub use events::{SigEvents, SigEventsFilter}; pub use events::{SigEvents, SigEventsFilter};
use ostd::{cpu::context::UserContext, user::UserContextApi}; use ostd::{cpu::context::UserContext, user::UserContextApi};
pub use pause::{with_signal_blocked, Pause}; pub use pause::{with_sigmask_changed, Pause};
pub use poll::{PollAdaptor, PollHandle, Pollable, Pollee, Poller}; pub use poll::{PollAdaptor, PollHandle, Pollable, Pollee, Poller};
use sig_action::{SigAction, SigActionFlags, SigDefaultAction}; use sig_action::{SigAction, SigActionFlags, SigDefaultAction};
use sig_mask::SigMask; use sig_mask::SigMask;

View File

@ -206,16 +206,22 @@ impl Pause for WaitQueue {
} }
} }
/// Executes a closure while temporarily blocking some signals for the current POSIX thread. /// Executes a closure after temporarily adjusting the signal mask of the current POSIX thread.
pub fn with_signal_blocked<R>(ctx: &Context, mask: SigMask, operate: impl FnOnce() -> R) -> R { pub fn with_sigmask_changed<R>(
let posix_thread = ctx.posix_thread; ctx: &Context,
let sig_mask = posix_thread.sig_mask(); mask_op: impl FnOnce(SigMask) -> SigMask,
operate: impl FnOnce() -> R,
) -> R {
let sig_mask = ctx.posix_thread.sig_mask();
// Save the original signal mask and apply the mask updates.
let old_mask = sig_mask.load(Ordering::Relaxed); let old_mask = sig_mask.load(Ordering::Relaxed);
sig_mask.store(old_mask + mask, Ordering::Relaxed); sig_mask.store(mask_op(old_mask), Ordering::Relaxed);
// Perform the operation.
let res = operate(); let res = operate();
// Restore the original signal mask.
sig_mask.store(old_mask, Ordering::Relaxed); sig_mask.store(old_mask, Ordering::Relaxed);
res res

View File

@ -2,13 +2,16 @@
#![expect(dead_code)] #![expect(dead_code)]
use super::{process_filter::ProcessFilter, signal::constants::SIGCHLD, ExitCode, Pid, Process}; use super::{
process_filter::ProcessFilter,
signal::{constants::SIGCHLD, with_sigmask_changed},
ExitCode, Pid, Process,
};
use crate::{ use crate::{
prelude::*, prelude::*,
process::{ process::{
posix_thread::{thread_table, AsPosixThread}, posix_thread::{thread_table, AsPosixThread},
process_table, process_table,
signal::with_signal_blocked,
}, },
}; };
@ -40,7 +43,10 @@ pub fn wait_child_exit(
ctx: &Context, ctx: &Context,
) -> Result<Option<Arc<Process>>> { ) -> Result<Option<Arc<Process>>> {
let current = ctx.process; let current = ctx.process;
let zombie_child = with_signal_blocked(ctx, SIGCHLD.into(), || { let zombie_child = with_sigmask_changed(
ctx,
|sigmask| sigmask + SIGCHLD,
|| {
current.children_wait_queue().pause_until(|| { current.children_wait_queue().pause_until(|| {
let unwaited_children = current let unwaited_children = current
.children() .children()
@ -84,7 +90,8 @@ pub fn wait_child_exit(
// wait // wait
None None
}) })
})??; },
)??;
Ok(zombie_child) Ok(zombie_child)
} }

View File

@ -73,6 +73,7 @@ use crate::syscall::{
pause::sys_pause, pause::sys_pause,
pipe::{sys_pipe, sys_pipe2}, pipe::{sys_pipe, sys_pipe2},
poll::sys_poll, poll::sys_poll,
ppoll::sys_ppoll,
prctl::sys_prctl, prctl::sys_prctl,
pread64::sys_pread64, pread64::sys_pread64,
preadv::{sys_preadv, sys_preadv2, sys_readv}, preadv::{sys_preadv, sys_preadv2, sys_readv},
@ -336,6 +337,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_FCHMODAT = 268 => sys_fchmodat(args[..3]); SYS_FCHMODAT = 268 => sys_fchmodat(args[..3]);
SYS_FACCESSAT = 269 => sys_faccessat(args[..3]); SYS_FACCESSAT = 269 => sys_faccessat(args[..3]);
SYS_PSELECT6 = 270 => sys_pselect6(args[..6]); SYS_PSELECT6 = 270 => sys_pselect6(args[..6]);
SYS_PPOLL = 271 => sys_ppoll(args[..5]);
SYS_SET_ROBUST_LIST = 273 => sys_set_robust_list(args[..2]); SYS_SET_ROBUST_LIST = 273 => sys_set_robust_list(args[..2]);
SYS_UTIMENSAT = 280 => sys_utimensat(args[..4]); SYS_UTIMENSAT = 280 => sys_utimensat(args[..4]);
SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..6]); SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..6]);

View File

@ -80,6 +80,7 @@ mod open;
mod pause; mod pause;
mod pipe; mod pipe;
mod poll; mod poll;
mod ppoll;
mod prctl; mod prctl;
mod pread64; mod pread64;
mod preadv; mod preadv;

View File

@ -10,10 +10,38 @@ use crate::{
file_table::{FileDesc, FileTable}, file_table::{FileDesc, FileTable},
}, },
prelude::*, prelude::*,
process::signal::Poller, process::{signal::Poller, ResourceType},
}; };
pub fn sys_poll(fds: Vaddr, nfds: u64, timeout: i32, ctx: &Context) -> Result<SyscallReturn> { pub fn sys_poll(fds: Vaddr, nfds: u32, timeout: i32, ctx: &Context) -> Result<SyscallReturn> {
let timeout = if timeout >= 0 {
Some(Duration::from_millis(timeout as _))
} else {
None
};
do_sys_poll(fds, nfds, timeout, ctx)
}
pub fn do_sys_poll(
fds: Vaddr,
nfds: u32,
timeout: Option<Duration>,
ctx: &Context,
) -> Result<SyscallReturn> {
if nfds as u64
> ctx
.process
.resource_limits()
.get_rlimit(ResourceType::RLIMIT_NOFILE)
.get_cur()
{
return_errno_with_message!(
Errno::EINVAL,
"the `nfds` value exceeds the `RLIMIT_NOFILE` value"
)
}
let user_space = ctx.user_space(); let user_space = ctx.user_space();
let poll_fds = { let poll_fds = {
@ -33,12 +61,6 @@ pub fn sys_poll(fds: Vaddr, nfds: u64, timeout: i32, ctx: &Context) -> Result<Sy
poll_fds poll_fds
}; };
let timeout = if timeout >= 0 {
Some(Duration::from_millis(timeout as _))
} else {
None
};
debug!( debug!(
"poll_fds = {:?}, nfds = {}, timeout = {:?}", "poll_fds = {:?}, nfds = {}, timeout = {:?}",
poll_fds, nfds, timeout poll_fds, nfds, timeout

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use super::{poll::do_sys_poll, SyscallReturn};
use crate::{
prelude::*,
process::signal::{sig_mask::SigMask, with_sigmask_changed},
time::timespec_t,
};
pub fn sys_ppoll(
fds: Vaddr,
nfds: u32,
timespec_addr: Vaddr,
sigmask_addr: Vaddr,
sigmask_size: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let timeout = if timespec_addr != 0 {
let time_spec = user_space.read_val::<timespec_t>(timespec_addr)?;
Some(Duration::try_from(time_spec)?)
} else {
None
};
if sigmask_addr != 0 {
if sigmask_size != size_of::<SigMask>() {
return_errno_with_message!(Errno::EINVAL, "invalid sigmask size");
}
let sigmask = user_space.read_val::<SigMask>(sigmask_addr)?;
with_sigmask_changed(ctx, |_| sigmask, || do_sys_poll(fds, nfds, timeout, ctx))
} else {
do_sys_poll(fds, nfds, timeout, ctx)
}
// TODO: Write back the remaining time to `timespec_addr`.
//
// The ppoll system call should write back the remaining time,
// yet the function counterpart in libc hides this behavior to
// make the API more portable across different UNIX-like OSes.
// For the maximized Linux compatibility, we should follow Linux's behavior.
// But this cannot be readily achieved given how our internal synchronization primitives
// such as `Pause` and `WaitTimeout` work.
}

View File

@ -1,10 +1,13 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::{sync::atomic::Ordering, time::Duration}; use core::time::Duration;
use super::{select::do_sys_select, SyscallReturn}; use super::{select::do_sys_select, SyscallReturn};
use crate::{ use crate::{
fs::file_table::FileDesc, prelude::*, process::signal::sig_mask::SigMask, time::timespec_t, fs::file_table::FileDesc,
prelude::*,
process::signal::{sig_mask::SigMask, with_sigmask_changed},
time::timespec_t,
}; };
pub fn sys_pselect6( pub fn sys_pselect6(
@ -17,45 +20,34 @@ pub fn sys_pselect6(
ctx: &Context, ctx: &Context,
) -> Result<SyscallReturn> { ) -> Result<SyscallReturn> {
let user_space = ctx.user_space(); let user_space = ctx.user_space();
let old_simask = if sigmask_addr != 0 {
let sigmask_with_size: SigMaskWithSize = user_space.read_val(sigmask_addr)?;
if !sigmask_with_size.is_valid() {
return_errno_with_message!(Errno::EINVAL, "sigmask size is invalid")
}
let old_sigmask = ctx
.posix_thread
.sig_mask()
.swap(sigmask_with_size.sigmask, Ordering::Relaxed);
Some(old_sigmask)
} else {
None
};
let timeout = if timespec_addr != 0 { let timeout = if timespec_addr != 0 {
let time_spec: timespec_t = user_space.read_val(timespec_addr)?; let time_spec = user_space.read_val::<timespec_t>(timespec_addr)?;
Some(Duration::try_from(time_spec)?) Some(Duration::try_from(time_spec)?)
} else { } else {
None None
}; };
let res = do_sys_select( let operate = || {
do_sys_select(
nfds, nfds,
readfds_addr, readfds_addr,
writefds_addr, writefds_addr,
exceptfds_addr, exceptfds_addr,
timeout, timeout,
ctx, ctx,
); )
};
if let Some(old_mask) = old_simask { if sigmask_addr != 0 {
ctx.posix_thread let sigmask_with_size = user_space.read_val::<SigMaskWithSize>(sigmask_addr)?;
.sig_mask() if !sigmask_with_size.is_valid() {
.store(old_mask, Ordering::Relaxed); return_errno_with_message!(Errno::EINVAL, "sigmask size is invalid")
}
with_sigmask_changed(ctx, |_: SigMask| sigmask_with_size.sigmask, operate)
} else {
operate()
} }
res
} }
#[repr(C)] #[repr(C)]

View File

@ -8,7 +8,7 @@ use crate::{
process::signal::{ process::signal::{
constants::{SIGKILL, SIGSTOP}, constants::{SIGKILL, SIGSTOP},
sig_mask::SigMask, sig_mask::SigMask,
with_signal_blocked, with_sigmask_changed,
}, },
}; };
@ -37,7 +37,11 @@ pub fn sys_rt_sigsuspend(
// Wait until receiving any signal // Wait until receiving any signal
let waiter = Waiter::new_pair().0; let waiter = Waiter::new_pair().0;
with_signal_blocked(ctx, sigmask, || waiter.pause_until(|| None::<()>))?; with_sigmask_changed(
ctx,
|old_mask| old_mask + sigmask,
|| waiter.pause_until(|| None::<()>),
)?;
// This syscall should always return `Err(EINTR)`. This path should never be reached. // This syscall should always return `Err(EINTR)`. This path should never be reached.
unreachable!("rt_sigsuspend always return EINTR"); unreachable!("rt_sigsuspend always return EINTR");

View File

@ -27,6 +27,7 @@ TESTS ?= \
mount_test \ mount_test \
open_create_test \ open_create_test \
open_test \ open_test \
ppoll_test \
prctl_setuid_test \ prctl_setuid_test \
pread64_test \ pread64_test \
preadv2_test \ preadv2_test \

View File

@ -0,0 +1,3 @@
PpollTest.NullFds
PpollTest.SignalMaskBlocksSignal
PpollTest.SignalMaskAllowsSignal