mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 09:23:25 +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 | ✅ |
|
| 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 | ❌ |
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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,51 +43,55 @@ 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(
|
||||||
current.children_wait_queue().pause_until(|| {
|
ctx,
|
||||||
let unwaited_children = current
|
|sigmask| sigmask + SIGCHLD,
|
||||||
.children()
|
|| {
|
||||||
.lock()
|
current.children_wait_queue().pause_until(|| {
|
||||||
.values()
|
let unwaited_children = current
|
||||||
.filter(|child| match child_filter {
|
.children()
|
||||||
ProcessFilter::Any => true,
|
.lock()
|
||||||
ProcessFilter::WithPid(pid) => child.pid() == pid,
|
.values()
|
||||||
ProcessFilter::WithPgid(pgid) => child.pgid() == pgid,
|
.filter(|child| match child_filter {
|
||||||
})
|
ProcessFilter::Any => true,
|
||||||
.cloned()
|
ProcessFilter::WithPid(pid) => child.pid() == pid,
|
||||||
.collect::<Vec<_>>();
|
ProcessFilter::WithPgid(pgid) => child.pgid() == pgid,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if unwaited_children.is_empty() {
|
if unwaited_children.is_empty() {
|
||||||
return Some(Err(Error::with_message(
|
return Some(Err(Error::with_message(
|
||||||
Errno::ECHILD,
|
Errno::ECHILD,
|
||||||
"the process has no child to wait",
|
"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 wait_options.contains(WaitOptions::WNOHANG) {
|
// return immediately if we find a zombie child
|
||||||
return Some(Ok(None));
|
let zombie_child = unwaited_children
|
||||||
}
|
.iter()
|
||||||
|
.find(|child| child.status().is_zombie());
|
||||||
|
|
||||||
// wait
|
if let Some(zombie_child) = zombie_child {
|
||||||
None
|
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)
|
Ok(zombie_child)
|
||||||
}
|
}
|
||||||
|
@ -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]);
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
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
|
// 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 = || {
|
||||||
nfds,
|
do_sys_select(
|
||||||
readfds_addr,
|
nfds,
|
||||||
writefds_addr,
|
readfds_addr,
|
||||||
exceptfds_addr,
|
writefds_addr,
|
||||||
timeout,
|
exceptfds_addr,
|
||||||
ctx,
|
timeout,
|
||||||
);
|
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)]
|
||||||
|
@ -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");
|
||||||
|
@ -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 \
|
||||||
|
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