mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Add syscall ppoll
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
1fe0fef410
commit
8815ca384f
@ -291,7 +291,7 @@ provided by Linux on x86-64 architecture.
|
||||
| 268 | fchmodat | ✅ |
|
||||
| 269 | faccessat | ✅ |
|
||||
| 270 | pselect6 | ✅ |
|
||||
| 271 | ppoll | ❌ |
|
||||
| 271 | ppoll | ✅ |
|
||||
| 272 | unshare | ❌ |
|
||||
| 273 | set_robust_list | ✅ |
|
||||
| 274 | get_robust_list | ❌ |
|
||||
|
@ -20,7 +20,7 @@ use c_types::{siginfo_t, ucontext_t};
|
||||
use constants::SIGSEGV;
|
||||
pub use events::{SigEvents, SigEventsFilter};
|
||||
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};
|
||||
use sig_action::{SigAction, SigActionFlags, SigDefaultAction};
|
||||
use sig_mask::SigMask;
|
||||
|
@ -206,16 +206,22 @@ impl Pause for WaitQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a closure while temporarily blocking some signals for the current POSIX thread.
|
||||
pub fn with_signal_blocked<R>(ctx: &Context, mask: SigMask, operate: impl FnOnce() -> R) -> R {
|
||||
let posix_thread = ctx.posix_thread;
|
||||
let sig_mask = posix_thread.sig_mask();
|
||||
/// Executes a closure after temporarily adjusting the signal mask of the current POSIX thread.
|
||||
pub fn with_sigmask_changed<R>(
|
||||
ctx: &Context,
|
||||
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);
|
||||
sig_mask.store(old_mask + mask, Ordering::Relaxed);
|
||||
sig_mask.store(mask_op(old_mask), Ordering::Relaxed);
|
||||
|
||||
// Perform the operation.
|
||||
let res = operate();
|
||||
|
||||
// Restore the original signal mask.
|
||||
sig_mask.store(old_mask, Ordering::Relaxed);
|
||||
|
||||
res
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
#![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::{
|
||||
prelude::*,
|
||||
process::{
|
||||
posix_thread::{thread_table, AsPosixThread},
|
||||
process_table,
|
||||
signal::with_signal_blocked,
|
||||
},
|
||||
};
|
||||
|
||||
@ -40,51 +43,55 @@ pub fn wait_child_exit(
|
||||
ctx: &Context,
|
||||
) -> Result<Option<Arc<Process>>> {
|
||||
let current = ctx.process;
|
||||
let zombie_child = with_signal_blocked(ctx, SIGCHLD.into(), || {
|
||||
current.children_wait_queue().pause_until(|| {
|
||||
let unwaited_children = current
|
||||
.children()
|
||||
.lock()
|
||||
.values()
|
||||
.filter(|child| match child_filter {
|
||||
ProcessFilter::Any => true,
|
||||
ProcessFilter::WithPid(pid) => child.pid() == pid,
|
||||
ProcessFilter::WithPgid(pgid) => child.pgid() == pgid,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let zombie_child = with_sigmask_changed(
|
||||
ctx,
|
||||
|sigmask| sigmask + SIGCHLD,
|
||||
|| {
|
||||
current.children_wait_queue().pause_until(|| {
|
||||
let unwaited_children = current
|
||||
.children()
|
||||
.lock()
|
||||
.values()
|
||||
.filter(|child| match child_filter {
|
||||
ProcessFilter::Any => true,
|
||||
ProcessFilter::WithPid(pid) => child.pid() == pid,
|
||||
ProcessFilter::WithPgid(pgid) => child.pgid() == pgid,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if unwaited_children.is_empty() {
|
||||
return Some(Err(Error::with_message(
|
||||
Errno::ECHILD,
|
||||
"the process has no child to wait",
|
||||
)));
|
||||
}
|
||||
|
||||
// return immediately if we find a zombie child
|
||||
let zombie_child = unwaited_children
|
||||
.iter()
|
||||
.find(|child| child.status().is_zombie());
|
||||
|
||||
if let Some(zombie_child) = zombie_child {
|
||||
let zombie_pid = zombie_child.pid();
|
||||
if wait_options.contains(WaitOptions::WNOWAIT) {
|
||||
// does not reap child, directly return
|
||||
return Some(Ok(Some(zombie_child.clone())));
|
||||
} else {
|
||||
reap_zombie_child(current, zombie_pid);
|
||||
return Some(Ok(Some(zombie_child.clone())));
|
||||
if unwaited_children.is_empty() {
|
||||
return Some(Err(Error::with_message(
|
||||
Errno::ECHILD,
|
||||
"the process has no child to wait",
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
if wait_options.contains(WaitOptions::WNOHANG) {
|
||||
return Some(Ok(None));
|
||||
}
|
||||
// return immediately if we find a zombie child
|
||||
let zombie_child = unwaited_children
|
||||
.iter()
|
||||
.find(|child| child.status().is_zombie());
|
||||
|
||||
// wait
|
||||
None
|
||||
})
|
||||
})??;
|
||||
if let Some(zombie_child) = zombie_child {
|
||||
let zombie_pid = zombie_child.pid();
|
||||
if wait_options.contains(WaitOptions::WNOWAIT) {
|
||||
// does not reap child, directly return
|
||||
return Some(Ok(Some(zombie_child.clone())));
|
||||
} else {
|
||||
reap_zombie_child(current, zombie_pid);
|
||||
return Some(Ok(Some(zombie_child.clone())));
|
||||
}
|
||||
}
|
||||
|
||||
if wait_options.contains(WaitOptions::WNOHANG) {
|
||||
return Some(Ok(None));
|
||||
}
|
||||
|
||||
// wait
|
||||
None
|
||||
})
|
||||
},
|
||||
)??;
|
||||
|
||||
Ok(zombie_child)
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ use crate::syscall::{
|
||||
pause::sys_pause,
|
||||
pipe::{sys_pipe, sys_pipe2},
|
||||
poll::sys_poll,
|
||||
ppoll::sys_ppoll,
|
||||
prctl::sys_prctl,
|
||||
pread64::sys_pread64,
|
||||
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_FACCESSAT = 269 => sys_faccessat(args[..3]);
|
||||
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_UTIMENSAT = 280 => sys_utimensat(args[..4]);
|
||||
SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..6]);
|
||||
|
@ -80,6 +80,7 @@ mod open;
|
||||
mod pause;
|
||||
mod pipe;
|
||||
mod poll;
|
||||
mod ppoll;
|
||||
mod prctl;
|
||||
mod pread64;
|
||||
mod preadv;
|
||||
|
@ -10,10 +10,38 @@ use crate::{
|
||||
file_table::{FileDesc, FileTable},
|
||||
},
|
||||
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 poll_fds = {
|
||||
@ -33,12 +61,6 @@ pub fn sys_poll(fds: Vaddr, nfds: u64, timeout: i32, ctx: &Context) -> Result<Sy
|
||||
poll_fds
|
||||
};
|
||||
|
||||
let timeout = if timeout >= 0 {
|
||||
Some(Duration::from_millis(timeout as _))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
debug!(
|
||||
"poll_fds = {:?}, nfds = {}, timeout = {:?}",
|
||||
poll_fds, nfds, timeout
|
||||
|
48
kernel/src/syscall/ppoll.rs
Normal file
48
kernel/src/syscall/ppoll.rs
Normal 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.
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
// 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 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(
|
||||
@ -17,45 +20,34 @@ pub fn sys_pselect6(
|
||||
ctx: &Context,
|
||||
) -> Result<SyscallReturn> {
|
||||
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 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)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res = do_sys_select(
|
||||
nfds,
|
||||
readfds_addr,
|
||||
writefds_addr,
|
||||
exceptfds_addr,
|
||||
timeout,
|
||||
ctx,
|
||||
);
|
||||
let operate = || {
|
||||
do_sys_select(
|
||||
nfds,
|
||||
readfds_addr,
|
||||
writefds_addr,
|
||||
exceptfds_addr,
|
||||
timeout,
|
||||
ctx,
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(old_mask) = old_simask {
|
||||
ctx.posix_thread
|
||||
.sig_mask()
|
||||
.store(old_mask, Ordering::Relaxed);
|
||||
if sigmask_addr != 0 {
|
||||
let sigmask_with_size = user_space.read_val::<SigMaskWithSize>(sigmask_addr)?;
|
||||
if !sigmask_with_size.is_valid() {
|
||||
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)]
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
process::signal::{
|
||||
constants::{SIGKILL, SIGSTOP},
|
||||
sig_mask::SigMask,
|
||||
with_signal_blocked,
|
||||
with_sigmask_changed,
|
||||
},
|
||||
};
|
||||
|
||||
@ -37,7 +37,11 @@ pub fn sys_rt_sigsuspend(
|
||||
|
||||
// Wait until receiving any signal
|
||||
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.
|
||||
unreachable!("rt_sigsuspend always return EINTR");
|
||||
|
@ -27,6 +27,7 @@ TESTS ?= \
|
||||
mount_test \
|
||||
open_create_test \
|
||||
open_test \
|
||||
ppoll_test \
|
||||
prctl_setuid_test \
|
||||
pread64_test \
|
||||
preadv2_test \
|
||||
|
3
test/syscall_test/blocklists/ppoll_test
Normal file
3
test/syscall_test/blocklists/ppoll_test
Normal file
@ -0,0 +1,3 @@
|
||||
PpollTest.NullFds
|
||||
PpollTest.SignalMaskBlocksSignal
|
||||
PpollTest.SignalMaskAllowsSignal
|
Reference in New Issue
Block a user