diff --git a/kernel/src/ipc/semaphore/system_v/sem.rs b/kernel/src/ipc/semaphore/system_v/sem.rs index 6b663e7a3..458538530 100644 --- a/kernel/src/ipc/semaphore/system_v/sem.rs +++ b/kernel/src/ipc/semaphore/system_v/sem.rs @@ -14,7 +14,10 @@ use crate::{ ipc::{key_t, semaphore::system_v::sem_set::sem_sets, IpcFlags}, prelude::*, process::Pid, - time::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout}, + time::{ + clocks::JIFFIES_TIMER_MANAGER, + timer::{Timeout, TimerGuard}, + }, }; #[derive(Clone, Copy, Debug, Pod)] @@ -170,10 +173,14 @@ pub fn sem_op( if let Some(timeout) = timeout { pending_op.waker = Some(waker.clone()); - let jiffies_timer = JIFFIES_TIMER_MANAGER.get().unwrap().create_timer(move || { - waker.wake_up(); - }); - jiffies_timer.set_timeout(Timeout::After(timeout)); + let jiffies_timer = + JIFFIES_TIMER_MANAGER + .get() + .unwrap() + .create_timer(move |_guard: TimerGuard| { + waker.wake_up(); + }); + jiffies_timer.lock().set_timeout(Timeout::After(timeout)); } else { pending_op.waker = Some(waker); } diff --git a/kernel/src/process/posix_thread/mod.rs b/kernel/src/process/posix_thread/mod.rs index 2ca42df9d..eb57d0f3c 100644 --- a/kernel/src/process/posix_thread/mod.rs +++ b/kernel/src/process/posix_thread/mod.rs @@ -22,7 +22,7 @@ use crate::{ Pid, }, thread::{Thread, Tid}, - time::{clocks::ProfClock, Timer, TimerManager}, + time::{clocks::ProfClock, timer::TimerGuard, Timer, TimerManager}, }; mod builder; @@ -261,7 +261,7 @@ impl PosixThread { /// Creates a timer based on the profiling CPU clock of the current thread. pub fn create_prof_timer(&self, func: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { self.prof_timer_manager.create_timer(func) } @@ -269,7 +269,7 @@ impl PosixThread { /// Creates a timer based on the user CPU clock of the current thread. pub fn create_virtual_timer(&self, func: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { self.virtual_timer_manager.create_timer(func) } diff --git a/kernel/src/process/process/timer_manager.rs b/kernel/src/process/process/timer_manager.rs index 81498799b..3bf50cb24 100644 --- a/kernel/src/process/process/timer_manager.rs +++ b/kernel/src/process/process/timer_manager.rs @@ -21,6 +21,7 @@ use crate::{ }, time::{ clocks::{ProfClock, RealTimeClock}, + timer::TimerGuard, Timer, TimerManager, }, }; @@ -99,7 +100,7 @@ pub struct PosixTimerManager { posix_timers: Mutex>>>, } -fn create_process_timer_callback(process_ref: &Weak) -> impl Fn() + Clone { +fn create_process_timer_callback(process_ref: &Weak) -> impl Fn(TimerGuard) + Clone { let current_process = process_ref.clone(); let sent_signal = move || { let signal = KernelSignal::new(SIGALRM); @@ -111,7 +112,7 @@ fn create_process_timer_callback(process_ref: &Weak) -> impl Fn() + Clo let work_func = Box::new(sent_signal); let work_item = WorkItem::new(work_func); - move || { + move |_guard: TimerGuard| { submit_work_item( work_item.clone(), crate::thread::work_queue::WorkPriority::High, @@ -158,7 +159,7 @@ impl PosixTimerManager { /// Creates a timer based on the profiling CPU clock of the current process. pub fn create_prof_timer(&self, func: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { self.prof_timer.timer_manager().create_timer(func) } @@ -166,7 +167,7 @@ impl PosixTimerManager { /// Creates a timer based on the user CPU clock of the current process. pub fn create_virtual_timer(&self, func: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { self.virtual_timer.timer_manager().create_timer(func) } diff --git a/kernel/src/process/signal/pause.rs b/kernel/src/process/signal/pause.rs index ea1d304cb..d2ba0b45f 100644 --- a/kernel/src/process/signal/pause.rs +++ b/kernel/src/process/signal/pause.rs @@ -9,7 +9,10 @@ use crate::{ prelude::*, process::{posix_thread::AsPosixThread, signal::HandlePendingSignal}, thread::AsThread, - time::wait::{ManagedTimeout, TimeoutExt}, + time::{ + timer::TimerGuard, + wait::{ManagedTimeout, TimeoutExt}, + }, }; /// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware. @@ -160,7 +163,7 @@ impl Pause for Waiter { fn pause_timeout(&self, timeout: &TimeoutExt<'_>) -> Result<()> { let timer = timeout.check_expired()?.map(|timeout| { let waker = self.waker(); - timeout.create_timer(move || { + timeout.create_timer(move |_guard: TimerGuard| { waker.wake_up(); }) }); @@ -189,11 +192,12 @@ impl Pause for Waiter { } if let Some(timer) = timer { - if timer.remain().is_zero() { + let timer_guard = timer.lock(); + if timer_guard.remain().is_zero() { return_errno_with_message!(Errno::ETIME, "the time limit is reached"); } // If the timeout is not expired, cancel the timer manually. - timer.cancel(); + timer_guard.cancel(); } if posix_thread_opt diff --git a/kernel/src/syscall/alarm.rs b/kernel/src/syscall/alarm.rs index c4ed3b57f..1b3a3db60 100644 --- a/kernel/src/syscall/alarm.rs +++ b/kernel/src/syscall/alarm.rs @@ -9,8 +9,9 @@ pub fn sys_alarm(seconds: u32, ctx: &Context) -> Result { debug!("seconds = {}", seconds); let alarm_timer = ctx.process.timer_manager().alarm_timer(); + let mut timer_guard = alarm_timer.lock(); - let remaining = alarm_timer.remain(); + let remaining = timer_guard.remain(); let mut remaining_secs = remaining.as_secs(); if remaining.subsec_nanos() > 0 { remaining_secs += 1; @@ -18,11 +19,11 @@ pub fn sys_alarm(seconds: u32, ctx: &Context) -> Result { if seconds == 0 { // Clear previous timer - alarm_timer.cancel(); + timer_guard.cancel(); return Ok(SyscallReturn::Return(remaining_secs as _)); } - alarm_timer.set_timeout(Timeout::After(Duration::from_secs(seconds as u64))); + timer_guard.set_timeout(Timeout::After(Duration::from_secs(seconds as u64))); Ok(SyscallReturn::Return(remaining_secs as _)) } diff --git a/kernel/src/syscall/setitimer.rs b/kernel/src/syscall/setitimer.rs index 6e7c7dab8..399234dc6 100644 --- a/kernel/src/syscall/setitimer.rs +++ b/kernel/src/syscall/setitimer.rs @@ -44,9 +44,22 @@ pub fn sys_setitimer( ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(), }; + let mut timer_guard = timer.lock(); + + let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain()); + + timer_guard.set_interval(interval); + if expire_time == Duration::ZERO { + // Clear previous timer + timer_guard.cancel(); + } else { + timer_guard.set_timeout(Timeout::After(expire_time)); + } + + drop(timer_guard); if old_itimerval_addr > 0 { - let old_interval = timeval_t::from(timer.interval()); - let remain = timeval_t::from(timer.remain()); + let old_interval = timeval_t::from(old_interval); + let remain = timeval_t::from(remain); let old_itimerval = itimerval_t { it_interval: old_interval, it_value: remain, @@ -54,14 +67,6 @@ pub fn sys_setitimer( user_space.write_val(old_itimerval_addr, &old_itimerval)?; } - timer.set_interval(interval); - if expire_time == Duration::ZERO { - // Clear previous timer - timer.cancel(); - } else { - timer.set_timeout(Timeout::After(expire_time)); - } - Ok(SyscallReturn::Return(0)) } @@ -86,8 +91,14 @@ pub fn sys_getitimer( ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(), }; - let interval = timeval_t::from(timer.interval()); - let remain = timeval_t::from(timer.remain()); + let (interval, remain) = { + let timer_guard = timer.lock(); + ( + timeval_t::from(timer_guard.interval()), + timeval_t::from(timer_guard.remain()), + ) + }; + let itimerval = itimerval_t { it_interval: interval, it_value: remain, diff --git a/kernel/src/syscall/timer_create.rs b/kernel/src/syscall/timer_create.rs index 13d7a7fe9..e96ab4c30 100644 --- a/kernel/src/syscall/timer_create.rs +++ b/kernel/src/syscall/timer_create.rs @@ -21,6 +21,7 @@ use crate::{ time::{ clockid_t, clocks::{BootTimeClock, MonotonicClock, RealTimeClock}, + timer::TimerGuard, Timer, }, }; @@ -97,7 +98,7 @@ pub fn sys_timer_create( let work_func = sent_signal; let work_item = WorkItem::new(work_func); - let func = move || { + let func = move |_guard: TimerGuard| { submit_work_item( work_item.clone(), crate::thread::work_queue::WorkPriority::High, @@ -117,7 +118,7 @@ pub fn sys_timer_delete(timer_id: usize, _ctx: &Context) -> Result Result(clockid: clockid_t, func: F, ctx: &Context) -> Result> where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { let process_timer_manager = ctx.process.timer_manager(); let timer = if clockid >= 0 { diff --git a/kernel/src/syscall/timer_settime.rs b/kernel/src/syscall/timer_settime.rs index 363cb0478..ee4b2cac6 100644 --- a/kernel/src/syscall/timer_settime.rs +++ b/kernel/src/syscall/timer_settime.rs @@ -28,27 +28,32 @@ pub fn sys_timer_settime( return_errno_with_message!(Errno::EINVAL, "invalid timer ID"); }; - 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)?; - } + let mut timer_guard = timer.lock(); - timer.set_interval(interval); + let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain()); + + timer_guard.set_interval(interval); if expire_time == Duration::ZERO { // Clear previous timer - timer.cancel(); + timer_guard.cancel(); } else { let timeout = if (flags & TIMER_ABSTIME) == 0 { Timeout::After(expire_time) } else { Timeout::When(expire_time) }; - timer.set_timeout(timeout); + timer_guard.set_timeout(timeout); + } + + drop(timer_guard); + if old_itimerspec_addr > 0 { + let old_interval = timespec_t::from(old_interval); + let remain = timespec_t::from(remain); + let old_itimerspec = itimerspec_t { + it_interval: old_interval, + it_value: remain, + }; + user_space.write_val(old_itimerspec_addr, &old_itimerspec)?; } Ok(SyscallReturn::Return(0)) @@ -66,8 +71,14 @@ pub fn sys_timer_gettime( return_errno_with_message!(Errno::EINVAL, "invalid timer ID"); }; - let interval = timespec_t::from(timer.interval()); - let remain = timespec_t::from(timer.remain()); + let (interval, remain) = { + let timer_guard = timer.lock(); + ( + timespec_t::from(timer_guard.interval()), + timespec_t::from(timer_guard.remain()), + ) + }; + let itimerspec = itimerspec_t { it_interval: interval, it_value: remain, diff --git a/kernel/src/syscall/timerfd_gettime.rs b/kernel/src/syscall/timerfd_gettime.rs index 6b3e8fdfe..1de2cefcb 100644 --- a/kernel/src/syscall/timerfd_gettime.rs +++ b/kernel/src/syscall/timerfd_gettime.rs @@ -23,11 +23,10 @@ pub fn sys_timerfd_gettime( .downcast_ref::() .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 (interval, remain) = timerfd_file.get_time(); let itimerspec = itimerspec_t { - it_interval: interval, - it_value: remain, + it_interval: timespec_t::from(interval), + it_value: timespec_t::from(remain), }; ctx.user_space().write_val(itimerspec_addr, &itimerspec)?; diff --git a/kernel/src/syscall/timerfd_settime.rs b/kernel/src/syscall/timerfd_settime.rs index 8cf94a852..3608229da 100644 --- a/kernel/src/syscall/timerfd_settime.rs +++ b/kernel/src/syscall/timerfd_settime.rs @@ -8,7 +8,6 @@ use crate::{ prelude::*, time::{ itimerspec_t, - timer::Timeout, timerfd::{TFDSetTimeFlags, TimerfdFile}, timespec_t, }, @@ -35,10 +34,10 @@ pub fn sys_timerfd_settime( let interval = Duration::try_from(new_itimerspec.it_interval)?; let expire_time = Duration::try_from(new_itimerspec.it_value)?; - let timer = timerfd_file.timer(); + let (old_interval, remain) = timerfd_file.set_time(expire_time, interval, flags); if old_itimerspec_addr > 0 { - let old_interval = timespec_t::from(timer.interval()); - let remain = timespec_t::from(timer.remain()); + let old_interval = timespec_t::from(old_interval); + let remain = timespec_t::from(remain); let old_itimerspec = itimerspec_t { it_interval: old_interval, it_value: remain, @@ -46,24 +45,5 @@ pub fn sys_timerfd_settime( 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(); - - if expire_time != Duration::ZERO { - if flags.contains(TFDSetTimeFlags::TFD_TIMER_CANCEL_ON_SET) { - unimplemented!() - } - - let timeout = if flags.contains(TFDSetTimeFlags::TFD_TIMER_ABSTIME) { - Timeout::When(expire_time) - } else { - Timeout::After(expire_time) - }; - timer.set_timeout(timeout); - } - Ok(SyscallReturn::Return(0)) } diff --git a/kernel/src/time/core/timer.rs b/kernel/src/time/core/timer.rs index f5c4a2cc7..1d5fc96e0 100644 --- a/kernel/src/time/core/timer.rs +++ b/kernel/src/time/core/timer.rs @@ -11,7 +11,7 @@ use core::{ time::Duration, }; -use ostd::sync::SpinLock; +use ostd::sync::{LocalIrqDisabled, SpinLock, SpinLockGuard}; use super::Clock; @@ -31,84 +31,79 @@ pub enum Timeout { /// its `interval` field with [`Timer::set_interval`]. By doing this, /// the timer will use the interval time to configure a new timing after expiration. pub struct Timer { - interval: SpinLock, + inner: SpinLock, timer_manager: Arc, - registered_callback: Box, - timer_callback: SpinLock>, + registered_callback: Box, } -impl Timer { - /// Create a `Timer` instance from a [`TimerManager`]. - /// This timer will be managed by the `TimerManager`. +#[derive(Default)] +struct TimerInner { + interval: Duration, + timer_callback: Weak, +} + +/// A guard that provides exclusive access to a `Timer`. +pub struct TimerGuard<'a> { + inner: SpinLockGuard<'a, TimerInner, LocalIrqDisabled>, + timer: &'a Arc, +} + +impl TimerGuard<'_> { + /// Sets the interval time for this timer. /// - /// Note that if the callback instructions involves sleep, users should put these instructions - /// into something like `WorkQueue` to avoid sleeping during system timer interruptions. - fn new(registered_callback: F, timer_manager: Arc) -> Arc - where - F: Fn() + Send + Sync + 'static, - { - Arc::new(Self { - interval: SpinLock::new(Duration::ZERO), - timer_manager, - registered_callback: Box::new(registered_callback), - timer_callback: SpinLock::new(Weak::default()), - }) - } - - /// Set the interval time for this timer. /// The timer will be reset with the interval time upon expiration. - pub fn set_interval(&self, interval: Duration) { - *self.interval.disable_irq().lock() = interval; + pub fn set_interval(&mut self, interval: Duration) { + self.inner.interval = interval; } - /// Cancel the current timer's set timeout callback. - pub fn cancel(&self) { - let timer_callback = self.timer_callback.disable_irq().lock(); - if let Some(timer_callback) = timer_callback.upgrade() { - timer_callback.cancel(); - } - } - - /// Set the timer with a timeout. + /// Sets the timer with a timeout. /// /// The registered callback function of this timer will be invoked /// when reaching timeout. If the timer has a valid interval, this timer /// will be set again with the interval when reaching timeout. - pub fn set_timeout(self: &Arc, timeout: Timeout) { + pub fn set_timeout(&mut self, timeout: Timeout) { let expired_time = match timeout { Timeout::After(timeout) => { - let now = self.timer_manager.clock.read_time(); + let now = self.timer.timer_manager.clock.read_time(); now + timeout } Timeout::When(timeout) => timeout, }; - let timer_weak = Arc::downgrade(self); - let new_timer_callback = Arc::new(TimerCallback::new( - expired_time, - Box::new(move || interval_timer_callback(&timer_weak)), - )); + let timer_weak = Arc::downgrade(self.timer); + let new_timer_callback = Arc::new(TimerCallback::new(expired_time, timer_weak)); - let mut timer_callback = self.timer_callback.disable_irq().lock(); - if let Some(timer_callback) = timer_callback.upgrade() { + if let Some(timer_callback) = self.inner.timer_callback.upgrade() { timer_callback.cancel(); } - *timer_callback = Arc::downgrade(&new_timer_callback); - self.timer_manager.insert(new_timer_callback); + + self.inner.timer_callback = Arc::downgrade(&new_timer_callback); + self.timer.timer_manager.insert(new_timer_callback); } - /// Return the current expired time of this timer. + /// Cancels the currently set `TimerCallback`. + /// + /// Once cancelled, the current `TimerCallback` will not be triggered again. + pub fn cancel(&self) { + if let Some(timer_callback) = self.inner.timer_callback.upgrade() { + timer_callback.cancel(); + } + } + + /// Returns the current expired time of this timer. pub fn expired_time(&self) -> Duration { - let timer_callback = self.timer_callback.disable_irq().lock().upgrade(); - timer_callback.map_or(Duration::ZERO, |timer_callback| timer_callback.expired_time) + let timer_callback = self.inner.timer_callback.upgrade(); + timer_callback + .and_then(|callback| (!callback.is_cancelled()).then_some(callback.expired_time)) + .unwrap_or(Duration::ZERO) } - /// Return the remain time to expiration of this timer. + /// Returns the remain time to expiration of this timer. /// /// If the timer has not been set, this method /// will return `Duration::ZERO`. pub fn remain(&self) -> Duration { - let now = self.timer_manager.clock.read_time(); + let now = self.timer.timer_manager.clock.read_time(); let expired_time = self.expired_time(); if expired_time > now { expired_time - now @@ -117,27 +112,41 @@ impl Timer { } } - /// Return a reference to the [`TimerManager`] which manages - /// the current timer. - pub fn timer_manager(&self) -> &Arc { - &self.timer_manager - } - /// Returns the interval time of the current timer. pub fn interval(&self) -> Duration { - *self.interval.disable_irq().lock() + self.inner.interval } } -fn interval_timer_callback(timer: &Weak) { - let Some(timer) = timer.upgrade() else { - return; - }; +impl Timer { + /// Creates a `Timer` instance from a [`TimerManager`]. + /// This timer will be managed by the `TimerManager`. + /// + /// Note that if the callback instructions involves sleep, users should put these instructions + /// into something like `WorkQueue` to avoid sleeping during system timer interruptions. + fn new(registered_callback: F, timer_manager: Arc) -> Arc + where + F: Fn(TimerGuard) + Send + Sync + 'static, + { + Arc::new(Self { + inner: SpinLock::new(TimerInner::default()), + timer_manager, + registered_callback: Box::new(registered_callback), + }) + } - (timer.registered_callback)(); - let interval = timer.interval.disable_irq().lock(); - if *interval != Duration::ZERO { - timer.set_timeout(Timeout::After(*interval)); + /// Locks the timer and returns a [`TimerGuard`] for exclusive access. + pub fn lock(self: &Arc) -> TimerGuard<'_> { + TimerGuard { + inner: self.inner.disable_irq().lock(), + timer: self, + } + } + + /// Returns a reference to the [`TimerManager`] which manages + /// the current timer. + pub fn timer_manager(&self) -> &Arc { + &self.timer_manager } } @@ -152,7 +161,7 @@ pub struct TimerManager { } impl TimerManager { - /// Create a `TimerManager` instance from a clock. + /// Creates a `TimerManager` instance from a clock. pub fn new(clock: Arc) -> Arc { Arc::new(Self { clock, @@ -183,8 +192,9 @@ impl TimerManager { .push(timer_callback); } - /// Check the managed timers, and if any have timed out, - /// call the corresponding callback functions. + /// Checks and processes the managed timers. + /// + /// If any of the timers have timed out, call the corresponding callback functions. pub fn process_expired_timers(&self) { let callbacks = { let mut timeout_list = self.timer_callbacks.disable_irq().lock(); @@ -208,14 +218,14 @@ impl TimerManager { }; for callback in callbacks { - (callback.callback)(); + callback.call(); } } - /// Create an [`Timer`], which will be managed by this `TimerManager`. + /// Creates an [`Timer`], which will be managed by this `TimerManager`. pub fn create_timer(self: &Arc, function: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { Timer::new(function, self.clone()) } @@ -224,30 +234,52 @@ impl TimerManager { /// A `TimerCallback` can be used to execute a timer callback function. struct TimerCallback { expired_time: Duration, - callback: Box, + timer: Weak, is_cancelled: AtomicBool, } impl TimerCallback { - /// Create an instance of `TimerCallback`. - fn new(timeout: Duration, callback: Box) -> Self { + /// Creates an instance of `TimerCallback`. + fn new(timeout: Duration, timer: Weak) -> Self { Self { expired_time: timeout, - callback, + timer, is_cancelled: AtomicBool::new(false), } } - /// Cancel a `TimerCallback`. If the callback function has not been called, + /// Cancels a `TimerCallback`. If the callback function has not been called, /// it will never be called again. fn cancel(&self) { self.is_cancelled.store(true, Ordering::Release); } - // Return whether the `TimerCallback` is cancelled. + // Returns whether the `TimerCallback` is cancelled. fn is_cancelled(&self) -> bool { self.is_cancelled.load(Ordering::Acquire) } + + fn call(&self) { + let Some(timer) = self.timer.upgrade() else { + return; + }; + + let mut timer_guard = timer.lock(); + + if self.is_cancelled() { + // The callback is cancelled. + return; + } + + let interval = timer_guard.interval(); + if interval != Duration::ZERO { + timer_guard.set_timeout(Timeout::After(interval)); + } + + // Pass the `timer_guard` guard to the callback, allowing it to prevent race conditions. + // The callback may choose to use the guard or drop it if not needed. + (timer.registered_callback)(timer_guard); + } } impl PartialEq for TimerCallback { diff --git a/kernel/src/time/timerfd.rs b/kernel/src/time/timerfd.rs index 42944a8d2..ad7336e3b 100644 --- a/kernel/src/time/timerfd.rs +++ b/kernel/src/time/timerfd.rs @@ -1,27 +1,40 @@ // SPDX-License-Identifier: MPL-2.0 -use core::sync::atomic::{AtomicU64, Ordering}; +use core::{ + fmt::Display, + sync::atomic::{AtomicU32, AtomicU64, Ordering}, + time::Duration, +}; + +use atomic_integer_wrapper::define_atomic_version_of_integer_like_type; use super::clockid_t; use crate::{ events::IoEvents, fs::{ file_handle::FileLike, + file_table::FdFlags, + path::RESERVED_MOUNT_ID, pseudofs::anon_inodefs_shared_inode, utils::{CreationFlags, Inode, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable, Pollee}, syscall::create_timer, - time::Timer, + time::{ + timer::{Timeout, TimerGuard}, + Timer, + }, }; /// A file-like object representing a timer that can be used with file descriptors. pub struct TimerfdFile { + clockid: clockid_t, timer: Arc, ticks: Arc, pollee: Pollee, - flags: SpinLock, + flags: AtomicTFDFlags, + settime_flags: AtomicTFDSetTimeFlags, } bitflags! { @@ -32,6 +45,28 @@ bitflags! { } } +// Required by `define_atomic_version_of_integer_like_type`. +impl TryFrom for TFDFlags { + type Error = Error; + + fn try_from(value: u32) -> Result { + Self::from_bits(value).ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid TFDFlags")) + } +} + +// Required by `define_atomic_version_of_integer_like_type`. +impl From for u32 { + fn from(value: TFDFlags) -> Self { + value.bits() + } +} + +define_atomic_version_of_integer_like_type!(TFDFlags, try_from = true, { + /// An atomic version of `TFDFlags`. + #[derive(Debug, Default)] + struct AtomicTFDFlags(AtomicU32); +}); + bitflags! { /// The flags used for timerfd settime operations. pub struct TFDSetTimeFlags: u32 { @@ -40,6 +75,29 @@ bitflags! { } } +// Required by `define_atomic_version_of_integer_like_type`. +impl TryFrom for TFDSetTimeFlags { + type Error = Error; + + fn try_from(value: u32) -> Result { + Self::from_bits(value) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid TFDSetTimeFlags")) + } +} + +// Required by `define_atomic_version_of_integer_like_type`. +impl From for u32 { + fn from(value: TFDSetTimeFlags) -> Self { + value.bits() + } +} + +define_atomic_version_of_integer_like_type!(TFDSetTimeFlags, try_from = true, { + /// An atomic version of `TFDSetTimeFlags`. + #[derive(Debug, Default)] + struct AtomicTFDSetTimeFlags(AtomicU32); +}); + impl TimerfdFile { /// Creates a new `TimerfdFile` instance. pub fn new(clockid: clockid_t, flags: TFDFlags, ctx: &Context) -> Result { @@ -50,37 +108,78 @@ impl TimerfdFile { let ticks = ticks.clone(); let pollee = pollee.clone(); - let expired_fn = move || { - ticks.fetch_add(1, Ordering::Release); + let expired_fn = move |_guard: TimerGuard| { + ticks.fetch_add(1, Ordering::Relaxed); pollee.notify(IoEvents::IN); }; create_timer(clockid, expired_fn, ctx) }?; Ok(TimerfdFile { + clockid, timer, ticks, pollee, - flags: SpinLock::new(flags), + flags: AtomicTFDFlags::new(flags), + settime_flags: AtomicTFDSetTimeFlags::default(), }) } - /// Gets the associated timer. - pub fn timer(&self) -> &Arc { - &self.timer + // Sets the timer's timeout and interval. + // + // The remaining time and old interval are saved before the settings are applied, and then + // returned afterwards. + pub fn set_time( + &self, + expire_time: Duration, + interval: Duration, + flags: TFDSetTimeFlags, + ) -> (Duration, Duration) { + let mut timer_guard = self.timer.lock(); + + let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain()); + + timer_guard.set_interval(interval); + + // Cancel the timer and clear the ticks counter. + timer_guard.cancel(); + self.ticks.store(0, Ordering::Relaxed); + + if expire_time != Duration::ZERO { + if flags.contains(TFDSetTimeFlags::TFD_TIMER_CANCEL_ON_SET) { + // TODO: Currently this flag has no effect since the system time cannot be changed. + // Once add the support for modifying the system time, the corresponding logics for + // this flag need to be implemented. + warn!("TFD_TIMER_CANCEL_ON_SET is not implemented yet and has no effect"); + } + + let timeout = if flags.contains(TFDSetTimeFlags::TFD_TIMER_ABSTIME) { + Timeout::When(expire_time) + } else { + Timeout::After(expire_time) + }; + + timer_guard.set_timeout(timeout); + self.settime_flags.store(flags, Ordering::Relaxed); + } + + (old_interval, remain) } - /// Clears the tick count. - pub fn clear_ticks(&self) { - self.ticks.store(0, Ordering::Release); + /// Gets the timer's remaining time and interval. + pub fn get_time(&self) -> (Duration, Duration) { + let timer_guard = self.timer.lock(); + (timer_guard.interval(), timer_guard.remain()) } fn is_nonblocking(&self) -> bool { - self.flags.lock().contains(TFDFlags::TFD_NONBLOCK) + self.flags + .load(Ordering::Relaxed) + .contains(TFDFlags::TFD_NONBLOCK) } fn try_read(&self, writer: &mut VmWriter) -> Result<()> { - let ticks = self.ticks.fetch_and(0, Ordering::AcqRel); + let ticks = self.ticks.fetch_and(0, Ordering::Relaxed); if ticks == 0 { return_errno_with_message!(Errno::EAGAIN, "the counter is zero"); @@ -94,7 +193,7 @@ impl TimerfdFile { fn check_io_events(&self) -> IoEvents { let mut events = IoEvents::empty(); - if self.ticks.load(Ordering::Acquire) != 0 { + if self.ticks.load(Ordering::Relaxed) != 0 { events |= IoEvents::IN; } @@ -139,13 +238,15 @@ impl FileLike for TimerfdFile { } 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; - } + self.flags + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |flags| { + if new_flags.contains(StatusFlags::O_NONBLOCK) { + Some(flags | TFDFlags::TFD_NONBLOCK) + } else { + Some(flags & !TFDFlags::TFD_NONBLOCK) + } + }) + .unwrap(); Ok(()) } @@ -153,4 +254,57 @@ impl FileLike for TimerfdFile { fn inode(&self) -> &Arc { anon_inodefs_shared_inode() } + + fn dump_proc_fdinfo(self: Arc, fd_flags: FdFlags) -> Box { + struct FdInfo { + flags: u32, + ino: u64, + clockid: i32, + ticks: u64, + settime_flags: u32, + it_value: Duration, + it_interval: Duration, + } + + impl Display for FdInfo { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "pos:\t{}", 0)?; + writeln!(f, "flags:\t0{:o}", self.flags)?; + // TODO: This should be the mount ID of the pseudo filesystem. + writeln!(f, "mnt_id:\t{}", RESERVED_MOUNT_ID)?; + writeln!(f, "ino:\t{}", self.ino)?; + writeln!(f, "clockid: {}", self.clockid)?; + writeln!(f, "ticks: {}", self.ticks)?; + writeln!(f, "settime flags: 0{:o}", self.settime_flags)?; + writeln!( + f, + "it_value: ({}, {})", + self.it_value.as_secs(), + self.it_value.subsec_nanos() + )?; + writeln!( + f, + "it_interval: ({}, {})", + self.it_interval.as_secs(), + self.it_interval.subsec_nanos() + ) + } + } + + let mut flags = self.status_flags().bits() | self.access_mode() as u32; + if fd_flags.contains(FdFlags::CLOEXEC) { + flags |= CreationFlags::O_CLOEXEC.bits(); + } + + let timer_guard = self.timer.lock(); + Box::new(FdInfo { + flags, + ino: self.inode().ino(), + clockid: self.clockid, + ticks: self.ticks.load(Ordering::Relaxed), + settime_flags: self.settime_flags.load(Ordering::Relaxed).bits(), + it_value: timer_guard.expired_time(), + it_interval: timer_guard.interval(), + }) + } } diff --git a/kernel/src/time/wait.rs b/kernel/src/time/wait.rs index db5e1201c..64d6fbda1 100644 --- a/kernel/src/time/wait.rs +++ b/kernel/src/time/wait.rs @@ -5,7 +5,7 @@ use core::time::Duration; use ostd::sync::{WaitQueue, Waiter}; use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout, Timer, TimerManager}; -use crate::prelude::*; +use crate::{prelude::*, time::timer::TimerGuard}; /// A trait that provide the timeout related function for [`Waiter`] and [`WaitQueue`]`. pub trait WaitTimeout { @@ -159,10 +159,10 @@ impl<'a> ManagedTimeout<'a> { /// Creates a timer for the timeout. pub fn create_timer(&self, callback: F) -> Arc where - F: Fn() + Send + Sync + 'static, + F: Fn(TimerGuard) + Send + Sync + 'static, { let timer = self.manager.create_timer(callback); - timer.set_timeout(self.timeout.clone()); + timer.lock().set_timeout(self.timeout.clone()); timer } } @@ -183,7 +183,7 @@ impl WaitTimeout for Waiter { let timer = timeout.map(|timeout| { let waker = self.waker(); - timeout.create_timer(move || { + timeout.create_timer(move |_guard: TimerGuard| { waker.wake_up(); }) }); @@ -194,7 +194,7 @@ impl WaitTimeout for Waiter { move || { if timer .as_ref() - .is_some_and(|timer| timer.remain() == Duration::ZERO) + .is_some_and(|timer| timer.lock().remain() == Duration::ZERO) { return_errno_with_message!(Errno::ETIME, "the time limit is reached"); } @@ -212,7 +212,7 @@ impl WaitTimeout for Waiter { .as_ref() .is_err_and(|e: &Error| e.error() == Errno::ETIME) { - timer.cancel(); + timer.lock().cancel(); } res diff --git a/kernel/src/vdso.rs b/kernel/src/vdso.rs index 30f72ebb4..df3a04c54 100644 --- a/kernel/src/vdso.rs +++ b/kernel/src/vdso.rs @@ -27,7 +27,11 @@ use spin::Once; use crate::{ syscall::ClockId, - time::{clocks::MonotonicClock, timer::Timeout, SystemTime, START_TIME}, + time::{ + clocks::MonotonicClock, + timer::{Timeout, TimerGuard}, + SystemTime, START_TIME, + }, vm::vmo::{Vmo, VmoOptions}, }; @@ -336,7 +340,7 @@ fn update_vdso_high_res_instant(instant: Instant, instant_cycles: u64) { } /// Updates instants with respect to coarse-resolution clocks in vDSO data. -fn update_vdso_coarse_res_instant() { +fn update_vdso_coarse_res_instant(_guard: TimerGuard) { let instant = Instant::from(read_monotonic_time()); VDSO.get().unwrap().update_coarse_res_instant(instant); } @@ -368,8 +372,9 @@ pub(super) fn init_in_first_kthread() { let coarse_instant_timer = ManuallyDrop::new( MonotonicClock::timer_manager().create_timer(update_vdso_coarse_res_instant), ); - coarse_instant_timer.set_interval(Duration::from_millis(100)); - coarse_instant_timer.set_timeout(Timeout::After(Duration::from_millis(100))); + let mut timer_guard = coarse_instant_timer.lock(); + timer_guard.set_interval(Duration::from_millis(100)); + timer_guard.set_timeout(Timeout::After(Duration::from_millis(100))); } /// Returns the vDSO VMO.