mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add sys_signalfd/sys_signalfd implementation
This commit is contained in:
parent
7644ca1598
commit
bce23a63bd
@ -302,14 +302,14 @@ provided by Linux on x86-64 architecture.
|
||||
| 279 | move_pages | ❌ |
|
||||
| 280 | utimensat | ✅ |
|
||||
| 281 | epoll_pwait | ✅ |
|
||||
| 282 | signalfd | ❌ |
|
||||
| 282 | signalfd | ✅ |
|
||||
| 283 | timerfd_create | ❌ |
|
||||
| 284 | eventfd | ✅ |
|
||||
| 285 | fallocate | ✅ |
|
||||
| 286 | timerfd_settime | ❌ |
|
||||
| 287 | timerfd_gettime | ❌ |
|
||||
| 288 | accept4 | ✅ |
|
||||
| 289 | signalfd4 | ❌ |
|
||||
| 289 | signalfd4 | ✅ |
|
||||
| 290 | eventfd2 | ✅ |
|
||||
| 291 | epoll_create1 | ✅ |
|
||||
| 292 | dup3 | ✅ |
|
||||
|
@ -13,6 +13,10 @@ impl SigEvents {
|
||||
pub fn new(sig_num: SigNum) -> Self {
|
||||
Self(sig_num)
|
||||
}
|
||||
|
||||
pub fn sig_num(&self) -> SigNum {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Events for SigEvents {}
|
||||
|
@ -118,6 +118,14 @@ impl<T: Into<SigSet>> ops::SubAssign<T> for SigSet {
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Not for SigSet {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self {
|
||||
SigSet { bits: !self.bits }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigSet {
|
||||
pub fn new_empty() -> Self {
|
||||
SigSet { bits: 0 }
|
||||
@ -139,9 +147,14 @@ impl SigSet {
|
||||
self.bits.count_ones() as usize
|
||||
}
|
||||
|
||||
pub fn contains(&self, set: impl Into<Self>) -> bool {
|
||||
let set = set.into();
|
||||
self.bits & set.bits == set.bits
|
||||
pub fn contains(&self, other: impl Into<Self>) -> bool {
|
||||
let other = other.into();
|
||||
self.bits & other.bits == other.bits
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: impl Into<Self>) -> bool {
|
||||
let other = other.into();
|
||||
self.bits & other.bits != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,7 @@ use crate::syscall::{
|
||||
setuid::sys_setuid,
|
||||
shutdown::sys_shutdown,
|
||||
sigaltstack::sys_sigaltstack,
|
||||
signalfd::sys_signalfd4,
|
||||
socket::sys_socket,
|
||||
socketpair::sys_socketpair,
|
||||
stat::{sys_fstat, sys_fstatat},
|
||||
@ -182,6 +183,7 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||
SYS_PWRITEV = 70 => sys_pwritev(args[..4]);
|
||||
SYS_SENDFILE64 = 71 => sys_sendfile(args[..4]);
|
||||
SYS_PSELECT6 = 72 => sys_pselect6(args[..6]);
|
||||
SYS_SIGNALFD4 = 74 => sys_signalfd4(args[..4]);
|
||||
SYS_READLINKAT = 78 => sys_readlinkat(args[..4]);
|
||||
SYS_NEWFSTATAT = 79 => sys_fstatat(args[..4]);
|
||||
SYS_NEWFSTAT = 80 => sys_fstat(args[..2]);
|
||||
|
@ -124,6 +124,7 @@ use crate::syscall::{
|
||||
setuid::sys_setuid,
|
||||
shutdown::sys_shutdown,
|
||||
sigaltstack::sys_sigaltstack,
|
||||
signalfd::{sys_signalfd, sys_signalfd4},
|
||||
socket::sys_socket,
|
||||
socketpair::sys_socketpair,
|
||||
stat::{sys_fstat, sys_fstatat, sys_lstat, sys_stat},
|
||||
@ -320,9 +321,11 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||
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]);
|
||||
SYS_SIGNALFD = 282 => sys_signalfd(args[..3]);
|
||||
SYS_EVENTFD = 284 => sys_eventfd(args[..1]);
|
||||
SYS_FALLOCATE = 285 => sys_fallocate(args[..4]);
|
||||
SYS_ACCEPT4 = 288 => sys_accept4(args[..4]);
|
||||
SYS_SIGNALFD4 = 289 => sys_signalfd4(args[..4]);
|
||||
SYS_EVENTFD2 = 290 => sys_eventfd2(args[..2]);
|
||||
SYS_EPOLL_CREATE1 = 291 => sys_epoll_create1(args[..1]);
|
||||
SYS_DUP3 = 292 => sys_dup3(args[..3]);
|
||||
|
@ -131,6 +131,7 @@ mod setsockopt;
|
||||
mod setuid;
|
||||
mod shutdown;
|
||||
mod sigaltstack;
|
||||
mod signalfd;
|
||||
mod socket;
|
||||
mod socketpair;
|
||||
mod stat;
|
||||
|
369
kernel/src/syscall/signalfd.rs
Normal file
369
kernel/src/syscall/signalfd.rs
Normal file
@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! signalfd implementation for Linux compatibility
|
||||
//!
|
||||
//! The signalfd mechanism allows receiving signals via file descriptor,
|
||||
//! enabling better integration with event loops.
|
||||
//! See https://man7.org/linux/man-pages/man2/signalfd.2.html
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
file_table::{get_file_fast, FdFlags, FileDesc},
|
||||
utils::{CreationFlags, InodeMode, InodeType, Metadata, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
posix_thread::AsPosixThread,
|
||||
signal::{
|
||||
constants::{SIGKILL, SIGSTOP},
|
||||
sig_mask::{AtomicSigMask, SigMask},
|
||||
signals::Signal,
|
||||
PollHandle, Pollable, Pollee, SigEvents, SigEventsFilter,
|
||||
},
|
||||
Gid, Uid,
|
||||
},
|
||||
time::clocks::RealTimeClock,
|
||||
};
|
||||
|
||||
/// Creates a new signalfd or updates an existing one according to the given mask
|
||||
pub fn sys_signalfd(
|
||||
fd: FileDesc,
|
||||
mask_ptr: Vaddr,
|
||||
sizemask: usize,
|
||||
ctx: &Context,
|
||||
) -> Result<SyscallReturn> {
|
||||
sys_signalfd4(fd, mask_ptr, sizemask, 0, ctx)
|
||||
}
|
||||
|
||||
/// Creates a new signalfd or updates an existing one according to the given mask and flags
|
||||
pub fn sys_signalfd4(
|
||||
fd: FileDesc,
|
||||
mask_ptr: Vaddr,
|
||||
sizemask: usize,
|
||||
flags: i32,
|
||||
ctx: &Context,
|
||||
) -> Result<SyscallReturn> {
|
||||
debug!(
|
||||
"fd = {}, mask = {:x}, sizemask = {}, flags = {}",
|
||||
fd, mask_ptr, sizemask, flags
|
||||
);
|
||||
|
||||
if sizemask != core::mem::size_of::<SigMask>() {
|
||||
return Err(Error::with_message(Errno::EINVAL, "invalid mask size"));
|
||||
}
|
||||
|
||||
let mut mask = ctx.user_space().read_val::<SigMask>(mask_ptr)?;
|
||||
mask -= SIGKILL;
|
||||
mask -= SIGSTOP;
|
||||
|
||||
let flags = SignalFileFlags::from_bits(flags as u32)
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid flags"))?;
|
||||
|
||||
let fd_flags = if flags.contains(SignalFileFlags::O_CLOEXEC) {
|
||||
FdFlags::CLOEXEC
|
||||
} else {
|
||||
FdFlags::empty()
|
||||
};
|
||||
|
||||
let non_blocking = flags.contains(SignalFileFlags::O_NONBLOCK);
|
||||
|
||||
let new_fd = if fd == -1 {
|
||||
create_new_signalfd(ctx, mask, non_blocking, fd_flags)?
|
||||
} else {
|
||||
update_existing_signalfd(ctx, fd, mask, non_blocking)?
|
||||
};
|
||||
|
||||
Ok(SyscallReturn::Return(new_fd as _))
|
||||
}
|
||||
|
||||
fn create_new_signalfd(
|
||||
ctx: &Context,
|
||||
mask: SigMask,
|
||||
non_blocking: bool,
|
||||
fd_flags: FdFlags,
|
||||
) -> Result<FileDesc> {
|
||||
let atomic_mask = AtomicSigMask::new(mask);
|
||||
let signal_file = SignalFile::new(atomic_mask, non_blocking);
|
||||
|
||||
register_observer(ctx, &signal_file, mask)?;
|
||||
|
||||
let file_table = ctx.thread_local.file_table().borrow();
|
||||
let fd = file_table.write().insert(signal_file, fd_flags);
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
fn update_existing_signalfd(
|
||||
ctx: &Context,
|
||||
fd: FileDesc,
|
||||
new_mask: SigMask,
|
||||
non_blocking: bool,
|
||||
) -> Result<FileDesc> {
|
||||
let mut file_table = ctx.thread_local.file_table().borrow_mut();
|
||||
let file = get_file_fast!(&mut file_table, fd);
|
||||
let signal_file = file
|
||||
.downcast_ref::<SignalFile>()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "File descriptor is not a signalfd"))?;
|
||||
|
||||
if signal_file.mask().load(Ordering::Relaxed) != new_mask {
|
||||
signal_file.update_signal_mask(new_mask)?;
|
||||
}
|
||||
signal_file.set_non_blocking(non_blocking);
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
fn register_observer(ctx: &Context, signal_file: &Arc<SignalFile>, mask: SigMask) -> Result<()> {
|
||||
let filter = SigEventsFilter::new(mask);
|
||||
|
||||
ctx.posix_thread
|
||||
.register_sigqueue_observer(signal_file.observer_ref(), filter);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Signal file descriptor creation flags
|
||||
struct SignalFileFlags: u32 {
|
||||
const O_CLOEXEC = CreationFlags::O_CLOEXEC.bits();
|
||||
const O_NONBLOCK = StatusFlags::O_NONBLOCK.bits();
|
||||
}
|
||||
}
|
||||
|
||||
/// Signal file implementation
|
||||
///
|
||||
/// Represents a file that can be used to receive signals
|
||||
/// as readable events.
|
||||
struct SignalFile {
|
||||
/// Atomic signal mask for filtering signals
|
||||
signals_mask: AtomicSigMask,
|
||||
/// I/O event notifier
|
||||
pollee: Pollee,
|
||||
/// Non-blocking mode flag
|
||||
non_blocking: AtomicBool,
|
||||
/// Weak reference to self as an observer
|
||||
weak_self: Weak<dyn Observer<SigEvents>>,
|
||||
}
|
||||
|
||||
impl SignalFile {
|
||||
/// Create a new signalfd instance
|
||||
fn new(mask: AtomicSigMask, non_blocking: bool) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_ref| {
|
||||
let weak_self = weak_ref.clone() as Weak<dyn Observer<SigEvents>>;
|
||||
Self {
|
||||
signals_mask: mask,
|
||||
pollee: Pollee::new(),
|
||||
non_blocking: AtomicBool::new(non_blocking),
|
||||
weak_self,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn mask(&self) -> &AtomicSigMask {
|
||||
&self.signals_mask
|
||||
}
|
||||
|
||||
fn observer_ref(&self) -> Weak<dyn Observer<SigEvents>> {
|
||||
self.weak_self.clone()
|
||||
}
|
||||
|
||||
fn update_signal_mask(&self, new_mask: SigMask) -> Result<()> {
|
||||
if let Some(thread) = current_thread!().as_posix_thread() {
|
||||
thread.unregister_sigqueue_observer(&self.weak_self);
|
||||
let filter = SigEventsFilter::new(new_mask);
|
||||
thread.register_sigqueue_observer(self.weak_self.clone(), filter);
|
||||
}
|
||||
self.signals_mask.store(new_mask, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_non_blocking(&self, non_blocking: bool) {
|
||||
self.non_blocking.store(non_blocking, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn is_non_blocking(&self) -> bool {
|
||||
self.non_blocking.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check current readable I/O events
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
let current = current_thread!();
|
||||
let Some(thread) = current.as_posix_thread() else {
|
||||
return IoEvents::empty();
|
||||
};
|
||||
|
||||
let mask = self.signals_mask.load(Ordering::Relaxed);
|
||||
if thread.sig_pending().intersects(mask) {
|
||||
IoEvents::IN
|
||||
} else {
|
||||
IoEvents::empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt non-blocking read operation
|
||||
fn try_read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
let current = current_thread!();
|
||||
let thread = current
|
||||
.as_posix_thread()
|
||||
.ok_or_else(|| Error::with_message(Errno::ESRCH, "Not a POSIX thread"))?;
|
||||
|
||||
// Mask is inverted to get the signals that are not blocked
|
||||
let mask = !self.signals_mask.load(Ordering::Relaxed);
|
||||
let max_signals = writer.avail() / core::mem::size_of::<SignalfdSiginfo>();
|
||||
let mut count = 0;
|
||||
|
||||
for _ in 0..max_signals {
|
||||
match thread.dequeue_signal(&mask) {
|
||||
Some(signal) => {
|
||||
writer.write_val(&signal.to_signalfd_siginfo())?;
|
||||
count += 1;
|
||||
self.pollee.invalidate();
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return_errno!(Errno::EAGAIN);
|
||||
}
|
||||
Ok(count * core::mem::size_of::<SignalfdSiginfo>())
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<SigEvents> for SignalFile {
|
||||
// TODO: Fix signal notifications.
|
||||
// Child processes do not inherit the parent's observer mechanism for signal event notifications.
|
||||
// `sys_poll` with blocking mode gets stuck if the signal is received after polling.
|
||||
fn on_events(&self, events: &SigEvents) {
|
||||
if self
|
||||
.signals_mask
|
||||
.load(Ordering::Relaxed)
|
||||
.contains(events.sig_num())
|
||||
{
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for SignalFile {
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||
self.pollee
|
||||
.poll_with(mask, poller, || self.check_io_events())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for SignalFile {
|
||||
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
|
||||
if writer.avail() < core::mem::size_of::<SignalfdSiginfo>() {
|
||||
return_errno_with_message!(Errno::EINVAL, "Buffer too small for siginfo structure");
|
||||
}
|
||||
|
||||
if self.is_non_blocking() {
|
||||
self.try_read(writer)
|
||||
} else {
|
||||
self.wait_events(IoEvents::IN, None, || self.try_read(writer))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, _reader: &mut VmReader) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EBADF, "signalfd does not support write operations");
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
if self.is_non_blocking() {
|
||||
StatusFlags::O_NONBLOCK
|
||||
} else {
|
||||
StatusFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
self.set_non_blocking(new_flags.contains(StatusFlags::O_NONBLOCK));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
let now = RealTimeClock::get().read_time();
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::NamedPipe,
|
||||
mode: InodeMode::from_bits_truncate(0o400),
|
||||
nlinks: 1,
|
||||
uid: Uid::new_root(),
|
||||
gid: Gid::new_root(),
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SignalFile {
|
||||
// TODO: Fix signal notifications. See `on_events` method.
|
||||
fn drop(&mut self) {
|
||||
if let Some(thread) = current_thread!().as_posix_thread() {
|
||||
thread.unregister_sigqueue_observer(&self.weak_self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct SignalfdSiginfo {
|
||||
ssi_signo: u32,
|
||||
ssi_errno: i32,
|
||||
ssi_code: i32,
|
||||
ssi_pid: u32,
|
||||
ssi_uid: u32,
|
||||
ssi_fd: i32,
|
||||
ssi_tid: u32,
|
||||
ssi_band: u32,
|
||||
ssi_overrun: u32,
|
||||
ssi_trapno: u32,
|
||||
ssi_status: i32,
|
||||
ssi_int: i32,
|
||||
ssi_ptr: u64,
|
||||
ssi_utime: u64,
|
||||
ssi_stime: u64,
|
||||
ssi_addr: u64,
|
||||
_pad: [u8; 48],
|
||||
}
|
||||
|
||||
trait ToSignalfdSiginfo {
|
||||
fn to_signalfd_siginfo(&self) -> SignalfdSiginfo;
|
||||
}
|
||||
|
||||
impl ToSignalfdSiginfo for Box<dyn Signal> {
|
||||
fn to_signalfd_siginfo(&self) -> SignalfdSiginfo {
|
||||
let siginfo = self.to_info();
|
||||
SignalfdSiginfo {
|
||||
ssi_signo: siginfo.si_signo as _,
|
||||
ssi_errno: siginfo.si_errno,
|
||||
ssi_code: siginfo.si_code,
|
||||
ssi_pid: 0,
|
||||
ssi_uid: 0,
|
||||
ssi_fd: 0,
|
||||
ssi_tid: 0,
|
||||
ssi_band: 0,
|
||||
ssi_overrun: 0,
|
||||
ssi_trapno: 0,
|
||||
ssi_status: 0,
|
||||
ssi_int: 0,
|
||||
ssi_ptr: 0,
|
||||
ssi_utime: 0,
|
||||
ssi_stime: 0,
|
||||
ssi_addr: 0,
|
||||
_pad: [0; 48],
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,7 @@ TESTS ?= \
|
||||
sendfile_test \
|
||||
sigaction_test \
|
||||
sigaltstack_test \
|
||||
signalfd_test \
|
||||
stat_test \
|
||||
stat_times_test \
|
||||
statfs_test \
|
||||
|
6
test/syscall_test/blocklists/signalfd_test
Normal file
6
test/syscall_test/blocklists/signalfd_test
Normal file
@ -0,0 +1,6 @@
|
||||
Signalfd.Ppoll
|
||||
Signalfd/SignalfdTest.Blocking/kSignoMax
|
||||
Signalfd/SignalfdTest.ThreadGroup/kSigno
|
||||
Signalfd/SignalfdTest.ThreadGroup/kSignoMax
|
||||
Signalfd/SignalfdTest.Poll/kSigno
|
||||
Signalfd/SignalfdTest.Poll/kSignoMax
|
Loading…
x
Reference in New Issue
Block a user