Support for timerfd-related syscalls

This commit is contained in:
Chen Chengjun
2025-04-25 16:16:46 +08:00
committed by Tate, Hongliang Tian
parent 34048c8cbc
commit 1775bb0861
10 changed files with 344 additions and 18 deletions

View File

@ -303,11 +303,11 @@ provided by Linux on x86-64 architecture.
| 280 | utimensat | ✅ |
| 281 | epoll_pwait | ✅ |
| 282 | signalfd | ✅ |
| 283 | timerfd_create | |
| 283 | timerfd_create | |
| 284 | eventfd | ✅ |
| 285 | fallocate | ✅ |
| 286 | timerfd_settime | |
| 287 | timerfd_gettime | |
| 286 | timerfd_settime | |
| 287 | timerfd_gettime | |
| 288 | accept4 | ✅ |
| 289 | signalfd4 | ✅ |
| 290 | eventfd2 | ✅ |

View File

@ -126,6 +126,9 @@ use crate::syscall::{
tgkill::sys_tgkill,
timer_create::{sys_timer_create, sys_timer_delete},
timer_settime::{sys_timer_gettime, sys_timer_settime},
timerfd_create::sys_timerfd_create,
timerfd_gettime::sys_timerfd_gettime,
timerfd_settime::sys_timerfd_settime,
truncate::{sys_ftruncate, sys_truncate},
umask::sys_umask,
umount::sys_umount,
@ -191,6 +194,7 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_SYNC = 81 => sys_sync(args[..0]);
SYS_FSYNC = 82 => sys_fsync(args[..1]);
SYS_FDATASYNC = 83 => sys_fdatasync(args[..1]);
SYS_TIMERFD_CREATE = 85 => sys_timerfd_create(args[..2]);
SYS_CAPGET = 90 => sys_capget(args[..2]);
SYS_CAPSET = 91 => sys_capset(args[..2]);
SYS_EXIT = 93 => sys_exit(args[..1]);
@ -293,6 +297,8 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_CLOCK_NANOSLEEP = 407 => sys_clock_nanosleep(args[..4]);
SYS_TIMER_GETTIME = 408 => sys_timer_gettime(args[..2]);
SYS_TIMER_SETTIME = 409 => sys_timer_settime(args[..4]);
SYS_TIMERFD_GETTIME = 410 => sys_timerfd_gettime(args[..2]);
SYS_TIMERFD_SETTIME = 411 => sys_timerfd_settime(args[..4]);
SYS_UTIMENSAT = 412 => sys_utimensat(args[..4]);
SYS_SEMTIMEDOP = 420 => sys_semtimedop(args[..4]);
SYS_CLONE3 = 435 => sys_clone3(args[..2], &user_ctx);

View File

@ -142,6 +142,9 @@ use crate::syscall::{
time::sys_time,
timer_create::{sys_timer_create, sys_timer_delete},
timer_settime::{sys_timer_gettime, sys_timer_settime},
timerfd_create::sys_timerfd_create,
timerfd_gettime::sys_timerfd_gettime,
timerfd_settime::sys_timerfd_settime,
truncate::{sys_ftruncate, sys_truncate},
umask::sys_umask,
umount::sys_umount,
@ -342,8 +345,11 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_UTIMENSAT = 280 => sys_utimensat(args[..4]);
SYS_EPOLL_PWAIT = 281 => sys_epoll_pwait(args[..6]);
SYS_SIGNALFD = 282 => sys_signalfd(args[..3]);
SYS_TIMERFD_CREATE = 283 => sys_timerfd_create(args[..2]);
SYS_EVENTFD = 284 => sys_eventfd(args[..1]);
SYS_FALLOCATE = 285 => sys_fallocate(args[..4]);
SYS_TIMERFD_SETTIME = 286 => sys_timerfd_settime(args[..4]);
SYS_TIMERFD_GETTIME = 287 => sys_timerfd_gettime(args[..2]);
SYS_ACCEPT4 = 288 => sys_accept4(args[..4]);
SYS_SIGNALFD4 = 289 => sys_signalfd4(args[..4]);
SYS_EVENTFD2 = 290 => sys_eventfd2(args[..2]);

View File

@ -4,6 +4,7 @@
//! The each sub module contains functions that handle real syscall logic.
pub use clock_gettime::ClockId;
use ostd::cpu::context::UserContext;
pub use timer_create::create_timer;
use crate::{context::Context, cpu::LinuxAbi, prelude::*};
@ -149,6 +150,9 @@ mod tgkill;
mod time;
mod timer_create;
mod timer_settime;
mod timerfd_create;
mod timerfd_gettime;
mod timerfd_settime;
mod truncate;
mod umask;
mod umount;

View File

@ -21,6 +21,7 @@ use crate::{
time::{
clockid_t,
clocks::{BootTimeClock, MonotonicClock, RealTimeClock},
Timer,
},
};
@ -103,7 +104,31 @@ pub fn sys_timer_create(
);
};
let process_timer_manager = current_process.timer_manager();
let timer = create_timer(clockid, func, ctx)?;
let timer_id = current_process.timer_manager().add_posix_timer(timer);
ctx.user_space().write_val(timer_id_addr, &timer_id)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_timer_delete(timer_id: usize, _ctx: &Context) -> Result<SyscallReturn> {
let current_process = current!();
let Some(timer) = current_process.timer_manager().remove_posix_timer(timer_id) else {
return_errno_with_message!(Errno::EINVAL, "invalid timer ID");
};
timer.cancel();
Ok(SyscallReturn::Return(0))
}
/// Creates a timer associated with the specified clock ID.
///
/// This timer will invoke the given callback function (`func`) when it expires.
pub fn create_timer<F>(clockid: clockid_t, func: F, ctx: &Context) -> Result<Arc<Timer>>
where
F: Fn() + Send + Sync + 'static,
{
let process_timer_manager = ctx.process.timer_manager();
let timer = if clockid >= 0 {
let clock_id = ClockId::try_from(clockid)?;
match clock_id {
@ -141,18 +166,5 @@ pub fn sys_timer_create(
DynamicClockIdInfo::Fd(_) => unimplemented!(),
}
};
let timer_id = process_timer_manager.add_posix_timer(timer);
ctx.user_space().write_val(timer_id_addr, &timer_id)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_timer_delete(timer_id: usize, _ctx: &Context) -> Result<SyscallReturn> {
let current_process = current!();
let Some(timer) = current_process.timer_manager().remove_posix_timer(timer_id) else {
return_errno_with_message!(Errno::EINVAL, "invalid timer ID");
};
timer.cancel();
Ok(SyscallReturn::Return(0))
Ok(timer)
}

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
fs::file_table::FdFlags,
prelude::*,
time::{
clockid_t,
timerfd::{TFDFlags, TimerfdFile},
},
};
pub fn sys_timerfd_create(clockid: clockid_t, flags: i32, ctx: &Context) -> Result<SyscallReturn> {
let flags = TFDFlags::from_bits(flags as u32)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown flags"))?;
let timerfd_file = TimerfdFile::new(clockid, flags, ctx)?;
let fd = {
let file_table = ctx.thread_local.borrow_file_table();
let mut file_table_locked = file_table.unwrap().write();
let fd_flags = if flags.contains(TFDFlags::TFD_CLOEXEC) {
FdFlags::CLOEXEC
} else {
FdFlags::empty()
};
file_table_locked.insert(Arc::new(timerfd_file), fd_flags)
};
Ok(SyscallReturn::Return(fd as _))
}

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
fs::file_table::FileDesc,
prelude::*,
time::{itimerspec_t, timerfd::TimerfdFile, timespec_t},
};
pub fn sys_timerfd_gettime(
fd: FileDesc,
itimerspec_addr: Vaddr,
ctx: &Context,
) -> Result<SyscallReturn> {
if itimerspec_addr == 0 {
return_errno_with_message!(Errno::EINVAL, "invalid pointer to return value");
}
let file_table = ctx.thread_local.borrow_file_table();
let file_table_locked = file_table.unwrap().read();
let timerfd_file = file_table_locked.get_file(fd as _)?;
let timerfd_file = timerfd_file
.downcast_ref::<TimerfdFile>()
.ok_or_else(|| Error::with_message(Errno::EINVAL, "the fd is not a timerfd"))?;
let interval = timespec_t::from(timerfd_file.timer().interval());
let remain = timespec_t::from(timerfd_file.timer().remain());
let itimerspec = itimerspec_t {
it_interval: interval,
it_value: remain,
};
ctx.user_space().write_val(itimerspec_addr, &itimerspec)?;
Ok(SyscallReturn::Return(0))
}

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use super::SyscallReturn;
use crate::{
fs::file_table::FileDesc,
prelude::*,
time::{itimerspec_t, timer::Timeout, timerfd::TimerfdFile, timespec_t, TIMER_ABSTIME},
};
pub fn sys_timerfd_settime(
fd: FileDesc,
flags: i32,
new_itimerspec_addr: Vaddr,
old_itimerspec_addr: Vaddr,
ctx: &Context,
) -> Result<SyscallReturn> {
let file_table = ctx.thread_local.borrow_file_table();
let file_table_locked = file_table.unwrap().read();
let timerfd_file = file_table_locked.get_file(fd as _)?;
let timerfd_file = timerfd_file
.downcast_ref::<TimerfdFile>()
.ok_or_else(|| Error::with_message(Errno::EINVAL, "the fd is not a timerfd"))?;
let user_space = ctx.user_space();
let new_itimerspec = user_space.read_val::<itimerspec_t>(new_itimerspec_addr)?;
let interval = Duration::try_from(new_itimerspec.it_interval)?;
let expire_time = Duration::try_from(new_itimerspec.it_value)?;
let timer = timerfd_file.timer();
if old_itimerspec_addr > 0 {
let old_interval = timespec_t::from(timer.interval());
let remain = timespec_t::from(timer.remain());
let old_itimerspec = itimerspec_t {
it_interval: old_interval,
it_value: remain,
};
user_space.write_val(old_itimerspec_addr, &old_itimerspec)?;
}
timer.set_interval(interval);
timer.cancel();
// Clear `ticks` after cancel the timer to ensure that `ticks` is zero
// when the timer is rearmed.
timerfd_file.clear_ticks();
const TFD_TIMER_CANCEL_ON_SET: i32 = 2;
if expire_time != Duration::ZERO {
let timeout = if flags == 0 {
Timeout::After(expire_time)
} else if flags == TIMER_ABSTIME {
Timeout::When(expire_time)
} else if flags == TFD_TIMER_CANCEL_ON_SET {
unimplemented!()
} else {
unreachable!()
};
timer.set_timeout(timeout);
}
Ok(SyscallReturn::Return(0))
}

View File

@ -14,6 +14,7 @@ pub mod clocks;
mod core;
mod softirq;
mod system_time;
pub mod timerfd;
pub mod wait;
pub type clockid_t = i32;

168
kernel/src/time/timerfd.rs Normal file
View File

@ -0,0 +1,168 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicU64, Ordering};
use super::clockid_t;
use crate::{
events::IoEvents,
fs::{
file_handle::FileLike,
utils::{CreationFlags, InodeMode, InodeType, Metadata, StatusFlags},
},
prelude::*,
process::{
signal::{PollHandle, Pollable, Pollee},
Gid, Uid,
},
syscall::create_timer,
time::{clocks::RealTimeClock, Timer},
};
/// A file-like object representing a timer that can be used with file descriptors.
pub struct TimerfdFile {
timer: Arc<Timer>,
ticks: Arc<AtomicU64>,
pollee: Pollee,
flags: SpinLock<TFDFlags>,
}
bitflags! {
/// The flags useds for timerfd-related operations.
pub struct TFDFlags: u32 {
const TFD_CLOEXEC = CreationFlags::O_CLOEXEC.bits();
const TFD_NONBLOCK = StatusFlags::O_NONBLOCK.bits();
}
}
impl TimerfdFile {
/// Creates a new `TimerfdFile` instance.
pub fn new(clockid: clockid_t, flags: TFDFlags, ctx: &Context) -> Result<Self> {
let ticks = Arc::new(AtomicU64::new(0));
let pollee = Pollee::new();
let timer = {
let ticks = ticks.clone();
let pollee = pollee.clone();
let expired_fn = move || {
ticks.fetch_add(1, Ordering::Release);
pollee.notify(IoEvents::IN);
};
create_timer(clockid, expired_fn, ctx)
}?;
Ok(TimerfdFile {
timer,
ticks,
pollee,
flags: SpinLock::new(flags),
})
}
/// Gets the associated timer.
pub fn timer(&self) -> &Arc<Timer> {
&self.timer
}
/// Clears the tick count.
pub fn clear_ticks(&self) {
self.ticks.store(0, Ordering::Release);
}
fn is_nonblocking(&self) -> bool {
self.flags.lock().contains(TFDFlags::TFD_NONBLOCK)
}
fn try_read(&self, writer: &mut VmWriter) -> Result<()> {
let ticks = self.ticks.fetch_and(0, Ordering::AcqRel);
if ticks == 0 {
return_errno_with_message!(Errno::EAGAIN, "the counter is zero");
}
writer.write_fallible(&mut ticks.as_bytes().into())?;
Ok(())
}
fn check_io_events(&self) -> IoEvents {
let mut events = IoEvents::empty();
if self.ticks.load(Ordering::Acquire) != 0 {
events |= IoEvents::IN;
}
events
}
}
impl Pollable for TimerfdFile {
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
self.pollee
.poll_with(mask, poller, || self.check_io_events())
}
}
impl FileLike for TimerfdFile {
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
let read_len = core::mem::size_of::<u64>();
if writer.avail() < read_len {
return_errno_with_message!(Errno::EINVAL, "buf len is less len u64 size");
}
if self.is_nonblocking() {
self.try_read(writer)?;
} else {
self.wait_events(IoEvents::IN, None, || self.try_read(writer))?;
}
Ok(read_len)
}
fn write(&self, _reader: &mut VmReader) -> Result<usize> {
return_errno_with_message!(Errno::EINVAL, "the file is not valid for writing");
}
fn status_flags(&self) -> StatusFlags {
if self.is_nonblocking() {
StatusFlags::O_NONBLOCK
} else {
StatusFlags::empty()
}
}
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
let mut flags = self.flags.lock();
if new_flags.contains(StatusFlags::O_NONBLOCK) {
*flags |= TFDFlags::TFD_NONBLOCK;
} else {
*flags &= !TFDFlags::TFD_NONBLOCK;
}
Ok(())
}
fn metadata(&self) -> Metadata {
// This is a dummy implementation.
// TODO: Add "anonymous inode fs" and link the file to it.
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(0o200),
nlinks: 1,
uid: Uid::new_root(),
gid: Gid::new_root(),
rdev: 0,
}
}
}