From 3c2b1a11c50c8c85a3042898f6b013b66b1c349e Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 25 Feb 2026 11:42:17 +0700 Subject: [PATCH] Add ordered lock for time offset mutex --- src/arch/aarch64/device/generic_timer.rs | 2 +- src/arch/aarch64/device/rtc.rs | 5 +++-- src/arch/aarch64/time.rs | 4 ++-- src/arch/riscv64/time.rs | 4 +++- src/arch/x86_shared/interrupt/irq.rs | 5 ++--- src/arch/x86_shared/stop.rs | 4 ++-- src/arch/x86_shared/time.rs | 6 ++++-- src/context/mod.rs | 3 ++- src/context/switch.rs | 2 +- src/context/timeout.rs | 4 ++-- src/scheme/dtb.rs | 2 +- src/scheme/sys/stat.rs | 2 +- src/scheme/time.rs | 4 ++-- src/sync/wait_queue.rs | 14 +++++++++----- src/syscall/debug.rs | 4 ++-- src/syscall/mod.rs | 9 ++++++--- src/syscall/time.rs | 10 +++++----- src/time.rs | 22 +++++++++++----------- 18 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/arch/aarch64/device/generic_timer.rs b/src/arch/aarch64/device/generic_timer.rs index 7d3b27d0b3..0bbb9346c2 100644 --- a/src/arch/aarch64/device/generic_timer.rs +++ b/src/arch/aarch64/device/generic_timer.rs @@ -128,7 +128,7 @@ impl InterruptHandler for GenericTimer { fn irq_handler(&mut self, irq: u32, token: &mut CleanLockToken) { self.clear_irq(); { - *time::OFFSET.lock() += self.clk_freq as u128; + *time::OFFSET.lock(token.token()) += self.clk_freq as u128; } timeout::trigger(token); diff --git a/src/arch/aarch64/device/rtc.rs b/src/arch/aarch64/device/rtc.rs index df1f334963..89f7e9e608 100644 --- a/src/arch/aarch64/device/rtc.rs +++ b/src/arch/aarch64/device/rtc.rs @@ -1,4 +1,4 @@ -use crate::{dtb::get_mmio_address, time}; +use crate::{dtb::get_mmio_address, sync::CleanLockToken, time}; use core::ptr::read_volatile; static RTC_DR: usize = 0x000; @@ -13,7 +13,8 @@ pub unsafe fn init(fdt: &fdt::Fdt) { Some(phys) => { let mut rtc = Pl031rtc { phys }; info!("PL031 RTC at {:#x}", rtc.phys); - *time::START.lock() = (rtc.time() as u128) * time::NANOS_PER_SEC; + let mut token = unsafe { CleanLockToken::new() }; + *time::START.lock(token.token()) = (rtc.time() as u128) * time::NANOS_PER_SEC; } None => { warn!("No PL031 RTC registers"); diff --git a/src/arch/aarch64/time.rs b/src/arch/aarch64/time.rs index 53b1911e87..53e62bb08a 100644 --- a/src/arch/aarch64/time.rs +++ b/src/arch/aarch64/time.rs @@ -1,6 +1,6 @@ -use crate::time::NANOS_PER_SEC; +use crate::{sync::CleanLockToken, time::NANOS_PER_SEC}; -pub fn monotonic_absolute() -> u128 { +pub fn monotonic_absolute(_token: &mut CleanLockToken) -> u128 { //TODO: aarch64 generic timer counter let ticks: usize; unsafe { core::arch::asm!("mrs {}, cntpct_el0", out(reg) ticks) }; diff --git a/src/arch/riscv64/time.rs b/src/arch/riscv64/time.rs index db1389b1da..f089112167 100644 --- a/src/arch/riscv64/time.rs +++ b/src/arch/riscv64/time.rs @@ -3,13 +3,15 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; +use crate::sync::CleanLockToken; + static MTIME_FREQ_HZ: AtomicUsize = AtomicUsize::new(0); pub fn init(freq_hz: usize) { MTIME_FREQ_HZ.store(freq_hz, Ordering::Relaxed); } -pub fn monotonic_absolute() -> u128 { +pub fn monotonic_absolute(_token: &mut CleanLockToken) -> u128 { let freq_hz = MTIME_FREQ_HZ.load(Ordering::Relaxed); if freq_hz > 0 { let counter: usize; diff --git a/src/arch/x86_shared/interrupt/irq.rs b/src/arch/x86_shared/interrupt/irq.rs index a25dbce45d..3853a47cfe 100644 --- a/src/arch/x86_shared/interrupt/irq.rs +++ b/src/arch/x86_shared/interrupt/irq.rs @@ -167,8 +167,9 @@ unsafe fn ioapic_unmask(irq: usize) { interrupt_stack!(pit_stack, |_stack| { // Saves CPU time by not sending IRQ event irq_trigger(0); + let mut token = unsafe { CleanLockToken::new() }; { - *time::OFFSET.lock() += pit::RATE; + *time::OFFSET.lock(token.token()) += pit::RATE; } unsafe { eoi(0) }; @@ -176,8 +177,6 @@ interrupt_stack!(pit_stack, |_stack| { // Wake up other CPUs ipi(IpiKind::Pit, IpiTarget::Other); - let mut token = unsafe { CleanLockToken::new() }; - // Any better way of doing this? timeout::trigger(&mut token); diff --git a/src/arch/x86_shared/stop.rs b/src/arch/x86_shared/stop.rs index 6187792a6a..df689d7302 100644 --- a/src/arch/x86_shared/stop.rs +++ b/src/arch/x86_shared/stop.rs @@ -69,7 +69,7 @@ fn userspace_acpi_shutdown(token: &mut CleanLockToken) { return; } info!("Waiting one second for ACPI driver to run the shutdown sequence."); - let initial = time::monotonic(); + let initial = time::monotonic(token); // Since this driver is a userspace process, and we do not use any magic like directly // context switching, we have to wait for the userspace driver to complete, with a timeout. @@ -81,7 +81,7 @@ fn userspace_acpi_shutdown(token: &mut CleanLockToken) { // TODO: Waitpid with timeout? Because, what if the ACPI driver would crash? let _ = context::switch(token); - let current = time::monotonic(); + let current = time::monotonic(token); if current - initial > time::NANOS_PER_SEC { info!("Timeout reached, thus falling back to other shutdown methods."); return; diff --git a/src/arch/x86_shared/time.rs b/src/arch/x86_shared/time.rs index 19f5172949..a08c786226 100644 --- a/src/arch/x86_shared/time.rs +++ b/src/arch/x86_shared/time.rs @@ -1,8 +1,10 @@ +use crate::sync::CleanLockToken; + #[cfg(feature = "acpi")] use super::device::hpet; use super::device::pit; -pub fn monotonic_absolute() -> u128 { +pub fn monotonic_absolute(token: &mut CleanLockToken) -> u128 { // The paravirtualized TSC is already guaranteed to be monotonic, and thus doesn't need to be // readjusted. #[cfg(feature = "x86_kvm_pv")] @@ -10,7 +12,7 @@ pub fn monotonic_absolute() -> u128 { return ns; } - *crate::time::OFFSET.lock() + hpet_or_pit() + *crate::time::OFFSET.lock(token.token()) + hpet_or_pit() } fn hpet_or_pit() -> u128 { #[cfg(feature = "acpi")] diff --git a/src/context/mod.rs b/src/context/mod.rs index 6960daebba..4b0b0c5caa 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -11,7 +11,8 @@ use crate::{ paging::{RmmA, RmmArch, TableKind}, percpu::PercpuBlock, sync::{ - ArcRwLockWriteGuard, CleanLockToken, L0, L1, L4, LockToken, RwLock, RwLockReadGuard, RwLockWriteGuard + ArcRwLockWriteGuard, CleanLockToken, LockToken, RwLock, RwLockReadGuard, RwLockWriteGuard, + L0, L1, L4, }, syscall::error::Result, }; diff --git a/src/context/switch.rs b/src/context/switch.rs index c8271389b8..1eb3338ff8 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -139,7 +139,7 @@ pub enum SwitchResult { /// - `SwitchResult::AllContextsIdle`: Indicates all contexts are idle, and the CPU will switch /// to an idle context. pub fn switch(token: &mut CleanLockToken) -> SwitchResult { - let switch_time = crate::time::monotonic(); + let switch_time = crate::time::monotonic(token); let percpu = PercpuBlock::current(); cpu_stats::add_context_switch(); diff --git a/src/context/timeout.rs b/src/context/timeout.rs index bb65b0370a..43f5bc8bfe 100644 --- a/src/context/timeout.rs +++ b/src/context/timeout.rs @@ -45,8 +45,8 @@ pub fn register( } pub fn trigger(token: &mut CleanLockToken) { - let mono = time::monotonic(); - let real = time::realtime(); + let mono = time::monotonic(token); + let real = time::realtime(token); let mut i = 0; loop { diff --git a/src/scheme/dtb.rs b/src/scheme/dtb.rs index 6f78088303..32e0aaf439 100644 --- a/src/scheme/dtb.rs +++ b/src/scheme/dtb.rs @@ -161,7 +161,7 @@ impl KernelScheme for DtbScheme { dst_buf.copy_common_bytes_from_slice(src_buf) } - fn kfpath(&self, id: usize, buf: UserSliceWo, token: &mut CleanLockToken) -> Result { + fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result { //TODO: construct useful path? buf.copy_common_bytes_from_slice("/scheme/kernel.dtb/".as_bytes()) } diff --git a/src/scheme/sys/stat.rs b/src/scheme/sys/stat.rs index 5daccc06c1..130ae89b94 100644 --- a/src/scheme/sys/stat.rs +++ b/src/scheme/sys/stat.rs @@ -10,7 +10,7 @@ use alloc::{string::String, vec::Vec}; /// Get the sys:stat data as displayed to the user. pub fn resource(token: &mut CleanLockToken) -> Result> { - let start_time_sec = *START.lock() / 1_000_000_000; + let start_time_sec = *START.lock(token.token()) / 1_000_000_000; let (contexts_running, contexts_blocked) = get_contexts_stats(token); let res = format!( diff --git a/src/scheme/time.rs b/src/scheme/time.rs index 64f493c661..e7bb1396e9 100644 --- a/src/scheme/time.rs +++ b/src/scheme/time.rs @@ -168,10 +168,10 @@ impl KernelScheme for TimeScheme { for current_chunk in buf.in_exact_chunks(mem::size_of::()) { let arch_time = match (handle.clock.clone(), handle.kind.clone()) { (CLOCK_REALTIME, TimeSchemeKind::Default | TimeSchemeKind::ClockGettime) => { - time::realtime() + time::realtime(token) } (CLOCK_MONOTONIC, TimeSchemeKind::Default | TimeSchemeKind::ClockGettime) => { - time::monotonic() + time::monotonic(token) } (CLOCK_REALTIME, TimeSchemeKind::ClockGetres) => time::realtime_resolution(), (CLOCK_MONOTONIC, TimeSchemeKind::ClockGetres) => time::monotonic_resolution(), diff --git a/src/sync/wait_queue.rs b/src/sync/wait_queue.rs index d63c295cd5..20be1809b0 100644 --- a/src/sync/wait_queue.rs +++ b/src/sync/wait_queue.rs @@ -71,7 +71,7 @@ impl WaitQueue { } } } -pub fn receive_into_user( + pub fn receive_into_user( &self, buf: UserSliceWo, block: bool, @@ -82,13 +82,16 @@ pub fn receive_into_user( loop { let mut preempt = PreemptGuard::new(¤t_context_ref, token); - + let mut inner = self.inner.lock(preempt.token().token()); if inner.is_empty() { if block { let (_, mut inner_token) = inner.token_split(); - if !self.condition.wait_setup(¤t_context_ref, reason, inner_token.token()) { + if !self + .condition + .wait_setup(¤t_context_ref, reason, inner_token.token()) + { return Err(Error::new(EINTR)); } @@ -97,8 +100,9 @@ pub fn receive_into_user( context::switch(token); - self.condition.wait_cleanup(¤t_context_ref, token.token()); - + self.condition + .wait_cleanup(¤t_context_ref, token.token()); + continue; } else if buf.is_empty() { return Ok(0); diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 3ffe1f6e72..2291c3dcab 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -258,7 +258,7 @@ pub fn debug_start([a, b, c, d, e, f, g]: [usize; 7], token: &mut CleanLockToken let string = format_call(a, b, c, d, e, f, g); println!("{}", string); - crate::time::monotonic() + crate::time::monotonic(token) } else { 0 }; @@ -290,7 +290,7 @@ pub fn debug_end( return; } let debug_duration = - debug_info.accumulated_time + (crate::time::monotonic() - debug_info.this_switch_time); + debug_info.accumulated_time + (crate::time::monotonic(token) - debug_info.this_switch_time); let context_lock = crate::context::current(); { diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 7a0c125cb1..137e4e8d99 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -228,9 +228,12 @@ pub fn syscall( token, ) .map(|()| 0), - SYS_CLOCK_GETTIME => { - clock_gettime(b, UserSlice::wo(c, core::mem::size_of::())?).map(|()| 0) - } + SYS_CLOCK_GETTIME => clock_gettime( + b, + UserSlice::wo(c, core::mem::size_of::())?, + token, + ) + .map(|()| 0), SYS_FUTEX => futex(b, c, d, e, f, token), SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d)).map(|()| 0), diff --git a/src/syscall/time.rs b/src/syscall/time.rs index 27e0e20993..6064fe135d 100644 --- a/src/syscall/time.rs +++ b/src/syscall/time.rs @@ -11,10 +11,10 @@ use crate::{ use super::usercopy::{UserSliceRo, UserSliceWo}; -pub fn clock_gettime(clock: usize, buf: UserSliceWo) -> Result<()> { +pub fn clock_gettime(clock: usize, buf: UserSliceWo, token: &mut CleanLockToken) -> Result<()> { let arch_time = match clock { - CLOCK_REALTIME => time::realtime(), - CLOCK_MONOTONIC => time::monotonic(), + CLOCK_REALTIME => time::realtime(token), + CLOCK_MONOTONIC => time::monotonic(token), _ => return Err(Error::new(EINVAL)), }; @@ -32,7 +32,7 @@ pub fn nanosleep( ) -> Result<()> { let req = unsafe { req_buf.read_exact::()? }; - let start = time::monotonic(); + let start = time::monotonic(token); let end = start + (req.tv_sec as u128 * time::NANOS_PER_SEC) + (req.tv_nsec as u128); let current_context = context::current(); @@ -56,7 +56,7 @@ pub fn nanosleep( let was_interrupted = current_context.write(token.token()).wake.take().is_some(); if let Some(rem_buf) = rem_buf_opt { - let current = time::monotonic(); + let current = time::monotonic(token); rem_buf.copy_exactly(&if current < end { let diff = end - current; diff --git a/src/time.rs b/src/time.rs index bd4bd71906..406ccc2411 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,7 +1,5 @@ -use spin::Mutex; - use crate::{ - sync::CleanLockToken, + sync::{CleanLockToken, Mutex, L1}, syscall::error::{Error, Result, EINVAL}, }; @@ -9,16 +7,18 @@ pub const NANOS_PER_SEC: u128 = 1_000_000_000; // TODO: seqlock? /// Kernel start time, measured in nanoseconds since Unix epoch -pub static START: Mutex = Mutex::new(0); +pub static START: Mutex = Mutex::new(0); /// Kernel up time, measured in nanoseconds since `START_TIME` -pub static OFFSET: Mutex = Mutex::new(0); +pub static OFFSET: Mutex = Mutex::new(0); -pub fn monotonic() -> u128 { - crate::arch::time::monotonic_absolute() +pub fn monotonic(token: &mut CleanLockToken) -> u128 { + crate::arch::time::monotonic_absolute(token) } -pub fn realtime() -> u128 { - *START.lock() + monotonic() +pub fn realtime(token: &mut CleanLockToken) -> u128 { + let start = { *START.lock(token.token()) }; + let offset = { monotonic(token) }; + start + offset } pub fn monotonic_resolution() -> u128 { @@ -29,8 +29,8 @@ pub fn realtime_resolution() -> u128 { monotonic_resolution() } -pub fn sys_update_time_offset(buf: &[u8], _token: &mut CleanLockToken) -> Result { +pub fn sys_update_time_offset(buf: &[u8], token: &mut CleanLockToken) -> Result { let start = <[u8; 16]>::try_from(buf).map_err(|_| Error::new(EINVAL))?; - *START.lock() = u128::from_ne_bytes(start); + *START.lock(token.token()) = u128::from_ne_bytes(start); Ok(16) }