Add abstractions for Clock and Timer

This commit is contained in:
Chen Chengjun
2024-05-16 15:45:44 +08:00
committed by Tate, Hongliang Tian
parent 0d5131c822
commit d019de29f9
21 changed files with 756 additions and 220 deletions

1
Cargo.lock generated
View File

@ -227,6 +227,7 @@ dependencies = [
"libflate", "libflate",
"log", "log",
"lru", "lru",
"paste",
"pod", "pod",
"rand", "rand",
"ringbuf", "ringbuf",

View File

@ -12,7 +12,7 @@ use x86::cpuid::cpuid;
use crate::{ use crate::{
arch::timer::{ arch::timer::{
pit::{self, OperatingMode}, pit::{self, OperatingMode},
TIMER_FREQ, TIMER_IRQ_NUM, TIMER_FREQ,
}, },
trap::IrqLine, trap::IrqLine,
}; };
@ -71,7 +71,7 @@ pub fn determine_tsc_freq_via_cpuid() -> Option<u64> {
/// the PIT to calculate this frequency. /// the PIT to calculate this frequency.
pub fn determine_tsc_freq_via_pit() -> u64 { pub fn determine_tsc_freq_via_pit() -> u64 {
// Allocate IRQ // Allocate IRQ
let mut irq = IrqLine::alloc_specific(TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap(); let mut irq = IrqLine::alloc().unwrap();
irq.on_active(pit_callback); irq.on_active(pit_callback);
// Enable PIT // Enable PIT

View File

@ -12,7 +12,7 @@ pub(crate) mod pci;
pub mod qemu; pub mod qemu;
#[cfg(feature = "intel_tdx")] #[cfg(feature = "intel_tdx")]
pub(crate) mod tdx_guest; pub(crate) mod tdx_guest;
pub(crate) mod timer; pub mod timer;
use core::{arch::x86_64::_rdtsc, sync::atomic::Ordering}; use core::{arch::x86_64::_rdtsc, sync::atomic::Ordering};

View File

@ -27,14 +27,16 @@ use crate::{
trap::IrqLine, trap::IrqLine,
}; };
pub fn init() { /// Init APIC with tsc deadline mode or periodic mode.
/// Return the corresponding `IrqLine` for the System Timer.
pub(super) fn init() -> IrqLine {
init_tsc_freq(); init_tsc_freq();
if is_tsc_deadline_mode_supported() { if is_tsc_deadline_mode_supported() {
info!("[Timer]: Enable APIC TSC deadline mode."); info!("[Timer]: Enable APIC TSC deadline mode.");
init_tsc_mode(); init_tsc_mode()
} else { } else {
info!("[Timer]: Enable APIC periodic mode."); info!("[Timer]: Enable APIC periodic mode.");
init_periodic_mode(); init_periodic_mode()
} }
} }
@ -47,10 +49,11 @@ fn is_tsc_deadline_mode_supported() -> bool {
(cpuid.ecx & TSC_DEADLINE_MODE_SUPPORT) > 0 (cpuid.ecx & TSC_DEADLINE_MODE_SUPPORT) > 0
} }
fn init_tsc_mode() { fn init_tsc_mode() -> IrqLine {
let timer_irq = IrqLine::alloc().unwrap();
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock_irq_disabled(); let mut apic_lock = APIC_INSTANCE.get().unwrap().lock_irq_disabled();
// Enable tsc deadline mode // Enable tsc deadline mode
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18)); apic_lock.set_lvt_timer(timer_irq.num() as u64 | (1 << 18));
drop(apic_lock); drop(apic_lock);
let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ; let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ;
@ -62,11 +65,13 @@ fn init_tsc_mode() {
callback.call(()); callback.call(());
APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback)); APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback));
timer_irq
} }
fn init_periodic_mode() { fn init_periodic_mode() -> IrqLine {
// Allocate IRQ // Allocate IRQ
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap(); let mut irq = IrqLine::alloc().unwrap();
irq.on_active(pit_callback); irq.on_active(pit_callback);
// Enable PIT // Enable PIT
@ -80,6 +85,8 @@ fn init_periodic_mode() {
drop(apic_lock); drop(apic_lock);
static IS_FINISH: AtomicBool = AtomicBool::new(false); static IS_FINISH: AtomicBool = AtomicBool::new(false);
static INIT_COUNT: AtomicU64 = AtomicU64::new(0);
x86_64::instructions::interrupts::enable(); x86_64::instructions::interrupts::enable();
while !IS_FINISH.load(Ordering::Acquire) { while !IS_FINISH.load(Ordering::Acquire) {
x86_64::instructions::hlt(); x86_64::instructions::hlt();
@ -87,6 +94,16 @@ fn init_periodic_mode() {
x86_64::instructions::interrupts::disable(); x86_64::instructions::interrupts::disable();
drop(irq); drop(irq);
// Init APIC Timer
let timer_irq = IrqLine::alloc().unwrap();
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock_irq_disabled();
apic_lock.set_timer_init_count(INIT_COUNT.load(Ordering::Relaxed));
apic_lock.set_lvt_timer(timer_irq.num() as u64 | (1 << 17));
apic_lock.set_timer_div_config(DivideConfig::Divide64);
return timer_irq;
fn pit_callback(trap_frame: &TrapFrame) { fn pit_callback(trap_frame: &TrapFrame) {
static IN_TIME: AtomicU64 = AtomicU64::new(0); static IN_TIME: AtomicU64 = AtomicU64::new(0);
static APIC_FIRST_COUNT: AtomicU64 = AtomicU64::new(0); static APIC_FIRST_COUNT: AtomicU64 = AtomicU64::new(0);
@ -108,17 +125,13 @@ fn init_periodic_mode() {
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock_irq_disabled(); let mut apic_lock = APIC_INSTANCE.get().unwrap().lock_irq_disabled();
let remain_ticks = apic_lock.timer_current_count(); let remain_ticks = apic_lock.timer_current_count();
apic_lock.set_timer_init_count(0); apic_lock.set_timer_init_count(0);
// Init APIC Timer
let ticks = (0xFFFF_FFFF - remain_ticks - APIC_FIRST_COUNT.load(Ordering::Relaxed)) let ticks = (0xFFFF_FFFF - remain_ticks - APIC_FIRST_COUNT.load(Ordering::Relaxed))
/ CALLBACK_TIMES; / CALLBACK_TIMES;
apic_lock.set_timer_init_count(ticks);
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 17));
apic_lock.set_timer_div_config(DivideConfig::Divide64);
info!( info!(
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz", "APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
ticks, remain_ticks, TIMER_FREQ ticks, remain_ticks, TIMER_FREQ
); );
INIT_COUNT.store(ticks, Ordering::Release);
IS_FINISH.store(true, Ordering::Release); IS_FINISH.store(true, Ordering::Release);
} }
} }

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: MPL-2.0
use core::{
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use super::TIMER_FREQ;
/// Jiffies is a term used to denote the units of time measurement by the kernel.
///
/// A jiffy represents one tick of the system timer interrupt,
/// whose frequency is equal to [`TIMER_FREQ`] Hz.
#[derive(Copy, Clone, Debug)]
pub struct Jiffies(u64);
pub(super) static ELAPSED: AtomicU64 = AtomicU64::new(0);
impl Jiffies {
/// Creates a new instance.
pub fn new(value: u64) -> Self {
Self(value)
}
/// Returns the elapsed time since the system boots up.
pub fn elapsed() -> Self {
Self::new(ELAPSED.load(Ordering::Relaxed))
}
/// Gets the number of jiffies.
pub fn as_u64(self) -> u64 {
self.0
}
/// Gets the `Duration` calculated from the jiffies counts.
pub fn as_duration(self) -> Duration {
Duration::from_millis(self.0 * 1000 / TIMER_FREQ)
}
}
impl From<Jiffies> for Duration {
fn from(value: Jiffies) -> Self {
value.as_duration()
}
}

View File

@ -1,20 +1,19 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
pub mod apic; mod apic;
pub mod hpet; mod hpet;
pub mod pit; mod jiffies;
pub(crate) mod pit;
use alloc::{boxed::Box, collections::BinaryHeap, sync::Arc, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::{ use core::{cell::RefCell, sync::atomic::Ordering};
any::Any,
sync::atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering},
};
pub use jiffies::Jiffies;
use spin::Once; use spin::Once;
use trapframe::TrapFrame; use trapframe::TrapFrame;
use self::apic::APIC_TIMER_CALLBACK; use self::apic::APIC_TIMER_CALLBACK;
use crate::{arch::x86::kernel, sync::SpinLock, trap::IrqLine}; use crate::{arch::x86::kernel, cpu_local, trap::IrqLine, CpuLocal};
/// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for unit conversion and /// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for unit conversion and
/// convenient for timer. What's more, the frequency cannot be set too high or too low, 1000Hz is /// convenient for timer. What's more, the frequency cannot be set too high or too low, 1000Hz is
@ -27,147 +26,50 @@ use crate::{arch::x86::kernel, sync::SpinLock, trap::IrqLine};
/// frequencies lower than 19Hz = 1193182 / 65536 (Timer rate / Divider) /// frequencies lower than 19Hz = 1193182 / 65536 (Timer rate / Divider)
pub const TIMER_FREQ: u64 = 1000; pub const TIMER_FREQ: u64 = 1000;
pub static TIMER_IRQ_NUM: AtomicU8 = AtomicU8::new(32);
pub static TICK: AtomicU64 = AtomicU64::new(0);
static TIMER_IRQ: Once<IrqLine> = Once::new(); static TIMER_IRQ: Once<IrqLine> = Once::new();
pub fn init() { pub(super) fn init() {
if kernel::apic::APIC_INSTANCE.is_completed() { /// In PIT mode, channel 0 is connected directly to IRQ0, which is
// Get the free irq number first. Use `allocate_target_irq` to get the Irq handle after dropping it. /// the `IrqLine` with the `irq_num` 32 (0-31 `IrqLine`s are reserved).
// Because the function inside `apic::init` will allocate this irq. ///
let irq = IrqLine::alloc().unwrap(); /// Ref: https://wiki.osdev.org/Programmable_Interval_Timer#Outputs.
TIMER_IRQ_NUM.store(irq.num(), Ordering::Relaxed); const PIT_MODE_TIMER_IRQ_NUM: u8 = 32;
drop(irq);
apic::init(); let mut timer_irq = if kernel::apic::APIC_INSTANCE.is_completed() {
apic::init()
} else { } else {
pit::init(pit::OperatingMode::SquareWaveGenerator); pit::init(pit::OperatingMode::SquareWaveGenerator);
IrqLine::alloc_specific(PIT_MODE_TIMER_IRQ_NUM).unwrap()
}; };
TIMEOUT_LIST.call_once(|| SpinLock::new(BinaryHeap::new()));
let mut timer_irq = IrqLine::alloc_specific(TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
timer_irq.on_active(timer_callback); timer_irq.on_active(timer_callback);
TIMER_IRQ.call_once(|| timer_irq); TIMER_IRQ.call_once(|| timer_irq);
} }
fn timer_callback(trap_frame: &TrapFrame) { cpu_local! {
let current_ticks = TICK.fetch_add(1, Ordering::SeqCst); static INTERRUPT_CALLBACKS: RefCell<Vec<Box<dyn Fn() + Sync + Send>>> = RefCell::new(Vec::new());
}
let callbacks = { /// Register a function that will be executed during the system timer interruption.
let mut callbacks = Vec::new(); pub fn register_callback<F>(func: F)
let mut timeout_list = TIMEOUT_LIST.get().unwrap().lock_irq_disabled(); where
F: Fn() + Sync + Send + 'static,
{
CpuLocal::borrow_with(&INTERRUPT_CALLBACKS, |callbacks| {
callbacks.borrow_mut().push(Box::new(func));
});
}
while let Some(t) = timeout_list.peek() { fn timer_callback(_: &TrapFrame) {
if t.is_cancelled() { jiffies::ELAPSED.fetch_add(1, Ordering::SeqCst);
// Just ignore the cancelled callback
timeout_list.pop(); CpuLocal::borrow_with(&INTERRUPT_CALLBACKS, |callbacks| {
} else if t.expire_ticks <= current_ticks { for callback in callbacks.borrow().iter() {
callbacks.push(timeout_list.pop().unwrap()); (callback)();
} else {
break;
}
} }
callbacks });
};
for callback in callbacks {
(callback.callback)(&callback);
}
if APIC_TIMER_CALLBACK.is_completed() { if APIC_TIMER_CALLBACK.is_completed() {
APIC_TIMER_CALLBACK.get().unwrap().call(()); APIC_TIMER_CALLBACK.get().unwrap().call(());
} }
} }
static TIMEOUT_LIST: Once<SpinLock<BinaryHeap<Arc<TimerCallback>>>> = Once::new();
pub struct TimerCallback {
expire_ticks: u64,
data: Arc<dyn Any + Send + Sync>,
callback: Box<dyn Fn(&TimerCallback) + Send + Sync>,
is_cancelled: AtomicBool,
}
impl TimerCallback {
fn new(
timeout_ticks: u64,
data: Arc<dyn Any + Send + Sync>,
callback: Box<dyn Fn(&TimerCallback) + Send + Sync>,
) -> Self {
Self {
expire_ticks: timeout_ticks,
data,
callback,
is_cancelled: AtomicBool::new(false),
}
}
pub fn data(&self) -> &Arc<dyn Any + Send + Sync> {
&self.data
}
/// Whether the set timeout is reached
pub fn is_expired(&self) -> bool {
let current_tick = TICK.load(Ordering::Acquire);
self.expire_ticks <= current_tick
}
/// Cancel a timer callback. If the callback function has not been called,
/// it will never be called again.
pub fn cancel(&self) {
self.is_cancelled.store(true, Ordering::Release);
}
// Whether the timer callback is cancelled.
fn is_cancelled(&self) -> bool {
self.is_cancelled.load(Ordering::Acquire)
}
}
impl PartialEq for TimerCallback {
fn eq(&self, other: &Self) -> bool {
self.expire_ticks == other.expire_ticks
}
}
impl Eq for TimerCallback {}
impl PartialOrd for TimerCallback {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TimerCallback {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.expire_ticks.cmp(&other.expire_ticks).reverse()
}
}
/// add timeout task into timeout list, the frequency see const TIMER_FREQ
///
/// user should ensure that the callback function cannot take too much time
///
pub fn add_timeout_list<F, T>(timeout: u64, data: T, callback: F) -> Arc<TimerCallback>
where
F: Fn(&TimerCallback) + Send + Sync + 'static,
T: Any + Send + Sync,
{
let timer_callback = TimerCallback::new(
TICK.load(Ordering::Acquire) + timeout,
Arc::new(data),
Box::new(callback),
);
let arc = Arc::new(timer_callback);
TIMEOUT_LIST
.get()
.unwrap()
.lock_irq_disabled()
.push(arc.clone());
arc
}
/// The time since the system boots up.
/// The currently returned results are in milliseconds.
pub fn read_monotonic_milli_seconds() -> u64 {
TICK.load(Ordering::Acquire) * (1000 / TIMER_FREQ)
}

View File

@ -16,7 +16,7 @@ pub type IrqCallbackFunction = dyn Fn(&TrapFrame) + Sync + Send + 'static;
/// An Interrupt ReQuest(IRQ) line. User can use `alloc` or `alloc_specific` to get specific IRQ line. /// An Interrupt ReQuest(IRQ) line. User can use `alloc` or `alloc_specific` to get specific IRQ line.
/// ///
/// The IRQ number is guaranteed to be external IRQ number and user can register callback functions to this IRQ resource. /// The IRQ number is guaranteed to be external IRQ number and user can register callback functions to this IRQ resource.
/// When this resrouce is dropped, all the callback in this will be unregistered automatically. /// When this resource is dropped, all the callback in this will be unregistered automatically.
#[derive(Debug)] #[derive(Debug)]
#[must_use] #[must_use]
pub struct IrqLine { pub struct IrqLine {

View File

@ -26,6 +26,7 @@ int-to-c-enum = { path = "../libs/int-to-c-enum" }
cpio-decoder = { path = "../libs/cpio-decoder" } cpio-decoder = { path = "../libs/cpio-decoder" }
ascii = { version = "1.1", default-features = false, features = ["alloc"] } ascii = { version = "1.1", default-features = false, features = ["alloc"] }
intrusive-collections = "0.9.5" intrusive-collections = "0.9.5"
paste = "1.0"
time = { version = "0.3", default-features = false, features = ["alloc"] } time = { version = "0.3", default-features = false, features = ["alloc"] }
smoltcp = { version = "0.9.1", default-features = false, features = [ smoltcp = { version = "0.9.1", default-features = false, features = [
"alloc", "alloc",

View File

@ -5,7 +5,7 @@ use core::{ops::Range, time::Duration};
use time::{OffsetDateTime, PrimitiveDateTime, Time}; use time::{OffsetDateTime, PrimitiveDateTime, Time};
use super::fat::ClusterID; use super::fat::ClusterID;
use crate::prelude::*; use crate::{prelude::*, time::clocks::RealTimeClock};
pub fn make_hash_index(cluster: ClusterID, offset: u32) -> usize { pub fn make_hash_index(cluster: ClusterID, offset: u32) -> usize {
(cluster as usize) << 32usize | (offset as usize & 0xffffffffusize) (cluster as usize) << 32usize | (offset as usize & 0xffffffffusize)
@ -59,8 +59,7 @@ impl DosTimestamp {
pub fn now() -> Result<Self> { pub fn now() -> Result<Self> {
#[cfg(not(ktest))] #[cfg(not(ktest))]
{ {
use crate::time::now_as_duration; DosTimestamp::from_duration(RealTimeClock::get().read_time())
DosTimestamp::from_duration(now_as_duration(&crate::time::ClockID::CLOCK_REALTIME)?)
} }
// When ktesting, the time module has not been initialized yet, return a fake value instead. // When ktesting, the time module has not been initialized yet, return a fake value instead.

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use aster_frame::timer::read_monotonic_milli_seconds; use aster_frame::arch::timer::Jiffies;
pub(super) fn get_network_timestamp() -> smoltcp::time::Instant { pub(super) fn get_network_timestamp() -> smoltcp::time::Instant {
let millis = read_monotonic_milli_seconds(); let millis = Jiffies::elapsed().as_duration().as_millis();
smoltcp::time::Instant::from_millis(millis as i64) smoltcp::time::Instant::from_millis(millis as i64)
} }

View File

@ -44,6 +44,7 @@ pub(crate) use crate::{
current, current_thread, current, current_thread,
error::{Errno, Error}, error::{Errno, Error},
print, println, print, println,
time::Clock,
}; };
pub(crate) type Result<T> = core::result::Result<T, Error>; pub(crate) type Result<T> = core::result::Result<T, Error>;
pub(crate) use crate::{return_errno, return_errno_with_message}; pub(crate) use crate::{return_errno, return_errno_with_message};

View File

@ -1,9 +1,21 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#![allow(non_camel_case_types)]
use core::time::Duration;
use int_to_c_enum::TryFromInt;
use super::SyscallReturn; use super::SyscallReturn;
use crate::{ use crate::{
prelude::*, prelude::*,
time::{clockid_t, now_as_duration, timespec_t, ClockID}, time::{
clockid_t,
clocks::{
BootTimeClock, MonotonicClock, MonotonicCoarseClock, MonotonicRawClock, RealTimeClock,
RealTimeCoarseClock,
},
timespec_t, Clock,
},
util::write_val_to_user, util::write_val_to_user,
}; };
@ -11,10 +23,40 @@ pub fn sys_clock_gettime(clockid: clockid_t, timespec_addr: Vaddr) -> Result<Sys
let clock_id = ClockID::try_from(clockid)?; let clock_id = ClockID::try_from(clockid)?;
debug!("clockid = {:?}", clock_id); debug!("clockid = {:?}", clock_id);
let time_duration = now_as_duration(&clock_id)?; let time_duration = read_clock(&clock_id)?;
let timespec = timespec_t::from(time_duration); let timespec = timespec_t::from(time_duration);
write_val_to_user(timespec_addr, &timespec)?; write_val_to_user(timespec_addr, &timespec)?;
Ok(SyscallReturn::Return(0)) Ok(SyscallReturn::Return(0))
} }
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
#[repr(i32)]
pub enum ClockID {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
CLOCK_PROCESS_CPUTIME_ID = 2,
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
/// Read the time of a clock specified by the input `ClockID`.
///
/// If the `ClockID` does not support, this function will return `Err`.
pub fn read_clock(clock_id: &ClockID) -> Result<Duration> {
match clock_id {
ClockID::CLOCK_REALTIME => Ok(RealTimeClock::get().read_time()),
ClockID::CLOCK_MONOTONIC => Ok(MonotonicClock::get().read_time()),
ClockID::CLOCK_MONOTONIC_RAW => Ok(MonotonicRawClock::get().read_time()),
ClockID::CLOCK_REALTIME_COARSE => Ok(RealTimeCoarseClock::get().read_time()),
ClockID::CLOCK_MONOTONIC_COARSE => Ok(MonotonicCoarseClock::get().read_time()),
ClockID::CLOCK_BOOTTIME => Ok(BootTimeClock::get().read_time()),
_ => {
return_errno_with_message!(Errno::EINVAL, "unsupported clock_id");
}
}
}

View File

@ -3,6 +3,7 @@
//! Read the Cpu context content then dispatch syscall to corrsponding handler //! Read the Cpu context content then dispatch syscall to corrsponding handler
//! The each sub module contains functions that handle real syscall logic. //! The each sub module contains functions that handle real syscall logic.
use aster_frame::cpu::UserContext; use aster_frame::cpu::UserContext;
pub use clock_gettime::ClockID;
use crate::prelude::*; use crate::prelude::*;

View File

@ -2,11 +2,11 @@
use core::time::Duration; use core::time::Duration;
use super::SyscallReturn; use super::{clock_gettime::read_clock, ClockID, SyscallReturn};
use crate::{ use crate::{
prelude::*, prelude::*,
process::signal::Pauser, process::signal::Pauser,
time::{clockid_t, now_as_duration, timespec_t, ClockID, TIMER_ABSTIME}, time::{clockid_t, timespec_t, TIMER_ABSTIME},
util::{read_val_from_user, write_val_to_user}, util::{read_val_from_user, write_val_to_user},
}; };
@ -58,7 +58,7 @@ fn do_clock_nanosleep(
clock_id, is_abs_time, request_time, remain_timespec_addr clock_id, is_abs_time, request_time, remain_timespec_addr
); );
let start_time = now_as_duration(&clock_id)?; let start_time = read_clock(&clock_id)?;
let timeout = if is_abs_time { let timeout = if is_abs_time {
if request_time < start_time { if request_time < start_time {
return Ok(SyscallReturn::Return(0)); return Ok(SyscallReturn::Return(0));
@ -77,7 +77,7 @@ fn do_clock_nanosleep(
match res { match res {
Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)), Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)),
Err(e) if e.error() == Errno::EINTR => { Err(e) if e.error() == Errno::EINTR => {
let end_time = now_as_duration(&clock_id)?; let end_time = read_clock(&clock_id)?;
if end_time >= start_time + timeout { if end_time >= start_time + timeout {
return Ok(SyscallReturn::Return(0)); return Ok(SyscallReturn::Return(0));

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
pub use system_wide::*;
mod system_wide;
pub(super) fn init() {
system_wide::init();
}

View File

@ -0,0 +1,294 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::sync::Arc;
use core::time::Duration;
use aster_frame::{
arch::timer::{self, Jiffies},
cpu_local,
sync::SpinLock,
CpuLocal,
};
use aster_time::read_monotonic_time;
use paste::paste;
use spin::Once;
use crate::time::{system_time::START_TIME_AS_DURATION, timer::TimerManager, Clock, SystemTime};
/// The Clock that reads the jiffies, and turn the counter into `Duration`.
pub struct JiffiesClock {
_private: (),
}
/// `RealTimeClock` represents a clock that provides the current real time.
pub struct RealTimeClock {
_private: (),
}
impl RealTimeClock {
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<RealTimeClock> {
CLOCK_REALTIME_INSTANCE.get().unwrap()
}
/// Get the cpu-local system-wide `TimerManager` singleton of this clock.
pub fn timer_manager() -> &'static Arc<TimerManager> {
CLOCK_REALTIME_MANAGER.get().unwrap()
}
}
/// `MonotonicClock` represents a clock that measures time in a way that is
/// monotonically increasing since the system was booted.
pub struct MonotonicClock {
_private: (),
}
impl MonotonicClock {
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<MonotonicClock> {
CLOCK_MONOTONIC_INSTANCE.get().unwrap()
}
/// Get the cpu-local system-wide `TimerManager` singleton of this clock.
pub fn timer_manager() -> &'static Arc<TimerManager> {
CLOCK_MONOTONIC_MANAGER.get().unwrap()
}
}
/// `RealTimeCoarseClock` is a coarse-grained version of a real-time clock.
///
/// This clock will maintain a record to `RealTimeClock`. This record
/// will be updated during each system timer interruption. Reading this clock
/// will directly reads the value of the record instead of calculating the time
/// based on the clocksource. Hence it is faster but less accurate.
///
/// Usually it will not be used to create a timer.
pub struct RealTimeCoarseClock {
_private: (),
}
impl RealTimeCoarseClock {
/// A reference to the current value of this clock.
fn current_ref() -> &'static Once<SpinLock<Duration>> {
static CURRENT: Once<SpinLock<Duration>> = Once::new();
&CURRENT
}
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<RealTimeCoarseClock> {
CLOCK_REALTIME_COARSE_INSTANCE.get().unwrap()
}
}
/// `MonotonicCoarseClock` is a coarse-grained version of the monotonic clock.
///
/// This clock is based on [`RealTimeCoarseClock`].
///
/// Usually it will not be used to create a timer.
pub struct MonotonicCoarseClock {
_private: (),
}
impl MonotonicCoarseClock {
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<MonotonicCoarseClock> {
CLOCK_MONOTONIC_COARSE_INSTANCE.get().unwrap()
}
}
/// `MonotonicRawClock` provides raw monotonic time that is not influenced by
/// NTP corrections.
///
/// Note: Currently we have not implement NTP corrections so we treat this clock
/// as the [`MonotonicClock`].
pub struct MonotonicRawClock {
_private: (),
}
impl MonotonicRawClock {
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<MonotonicRawClock> {
CLOCK_MONOTONIC_RAW_INSTANCE.get().unwrap()
}
}
/// `BootTimeClock` measures the time elapsed since the system was booted,
/// including time when the system was suspended.
///
/// Note: currently the system will not be suspended so we treat this clock
/// as the [`MonotonicClock`].
pub struct BootTimeClock {
_private: (),
}
impl BootTimeClock {
/// Get the singleton of this clock.
pub fn get() -> &'static Arc<BootTimeClock> {
CLOCK_BOOTTIME_INSTANCE.get().unwrap()
}
}
impl Clock for JiffiesClock {
fn read_time(&self) -> Duration {
Jiffies::elapsed().as_duration()
}
}
impl Clock for RealTimeClock {
fn read_time(&self) -> Duration {
SystemTime::now()
.duration_since(&SystemTime::UNIX_EPOCH)
.unwrap()
}
}
impl Clock for MonotonicClock {
fn read_time(&self) -> Duration {
read_monotonic_time()
}
}
impl Clock for RealTimeCoarseClock {
fn read_time(&self) -> Duration {
*Self::current_ref().get().unwrap().lock_irq_disabled()
}
}
impl Clock for MonotonicCoarseClock {
fn read_time(&self) -> Duration {
RealTimeCoarseClock::get().read_time() - *START_TIME_AS_DURATION.get().unwrap()
}
}
impl Clock for MonotonicRawClock {
fn read_time(&self) -> Duration {
read_monotonic_time()
}
}
impl Clock for BootTimeClock {
fn read_time(&self) -> Duration {
read_monotonic_time()
}
}
/// Define the system-wide clocks.
macro_rules! define_system_clocks {
($($clock_id:ident => $clock_type:ident,)*) => {
$(
paste! {
pub static [<$clock_id _INSTANCE>]: Once<Arc<$clock_type>> = Once::new();
}
)*
fn _init_system_wide_clocks() {
$(
let clock = Arc::new(
$clock_type {
_private: (),
}
);
paste! {
[<$clock_id _INSTANCE>].call_once(|| clock.clone());
}
)*
}
}
}
/// Define the timer managers of some system-wide clocks.
macro_rules! define_timer_managers {
($($clock_id:ident,)*) => {
$(
paste! {
cpu_local! {
pub static [<$clock_id _MANAGER>]: Once<Arc<TimerManager>> = Once::new();
}
}
)*
fn _init_system_wide_timer_managers() {
$(
let clock = paste! {[<$clock_id _INSTANCE>].get().unwrap().clone()};
let clock_manager = TimerManager::new(clock);
paste! {
CpuLocal::borrow_with(&[<$clock_id _MANAGER>], |manager| {
manager.call_once(|| clock_manager.clone());
});
}
let callback = move || {
clock_manager.process_expired_timers();
};
timer::register_callback(callback);
)*
}
}
}
define_system_clocks! {
CLOCK_REALTIME => RealTimeClock,
CLOCK_REALTIME_COARSE => RealTimeCoarseClock,
CLOCK_MONOTONIC => MonotonicClock,
CLOCK_MONOTONIC_COARSE => MonotonicCoarseClock,
CLOCK_MONOTONIC_RAW => MonotonicRawClock,
CLOCK_BOOTTIME => BootTimeClock,
}
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC,];
/// Init the system-wide clocks.
fn init_system_wide_clocks() {
_init_system_wide_clocks();
}
/// Init the system-wide cpu-local [`TimerManager`]s.
fn init_system_wide_timer_managers() {
_init_system_wide_timer_managers();
}
/// The system-wide [`TimerManager`] for the [`JiffiesClock`].
pub static JIFFIES_TIMER_MANAGER: Once<Arc<TimerManager>> = Once::new();
fn init_jiffies_clock_manager() {
let jiffies_clock = JiffiesClock { _private: () };
let jiffies_timer_manager = TimerManager::new(Arc::new(jiffies_clock));
JIFFIES_TIMER_MANAGER.call_once(|| jiffies_timer_manager.clone());
let callback = move || {
jiffies_timer_manager.process_expired_timers();
};
timer::register_callback(callback);
}
fn update_coarse_clock() {
let real_time = RealTimeClock::get().read_time();
let current = RealTimeCoarseClock::current_ref().get().unwrap();
*current.lock_irq_disabled() = real_time;
}
fn init_coarse_clock() {
let real_time = RealTimeClock::get().read_time();
RealTimeCoarseClock::current_ref().call_once(|| SpinLock::new(real_time));
timer::register_callback(update_coarse_clock);
}
pub(super) fn init() {
init_system_wide_clocks();
init_system_wide_timer_managers();
init_jiffies_clock_manager();
init_coarse_clock();
}
#[cfg(ktest)]
/// Init `CLOCK_REALTIME_MANAGER` for process-related ktests.
///
/// TODO: `ktest` may require a feature that allows the registration of initialization functions
/// to avoid functions like this one.
pub fn init_for_ktest() {
// If `spin::Once` has initialized, this closure will not be executed.
CLOCK_REALTIME_MANAGER.call_once(|| {
let clock = RealTimeClock { _private: () };
TimerManager::new(Arc::new(clock))
});
}

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use aster_frame::arch::timer::TIMER_FREQ;
use aster_time::NANOS_PER_SECOND;
pub mod timer;
type Nanos = u64;
/// A trait that can abstract clocks which have the ability to read time,
/// and has a fixed resolution.
pub trait Clock: Send + Sync {
/// Read the current time of this clock.
fn read_time(&self) -> Duration;
/// Return the resolution of this clock.
/// Set to the resolution of system time interrupt by default.
fn resolution(&self) -> Nanos
where
Self: Sized,
{
NANOS_PER_SECOND as u64 / TIMER_FREQ
}
}

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{
boxed::Box,
collections::BinaryHeap,
sync::{Arc, Weak},
vec::Vec,
};
use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use aster_frame::sync::SpinLock;
use super::Clock;
/// A timer with periodic functionality.
///
/// Setting the timer will trigger a callback function upon expiration of
/// the set time. To enable its periodic functionality, users should set
/// 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<Duration>,
timer_manager: Arc<TimerManager>,
registered_callback: Box<dyn Fn() + Send + Sync>,
timer_callback: SpinLock<Weak<TimerCallback>>,
}
impl Timer {
/// Create 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<F>(registered_callback: F, timer_manager: Arc<TimerManager>) -> Arc<Self>
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.lock_irq_disabled() = interval;
}
/// Cancel the current timer's set timeout callback.
pub fn cancel(&self) {
let timer_callback = self.timer_callback.lock_irq_disabled();
if let Some(timer_callback) = timer_callback.upgrade() {
timer_callback.cancel();
}
}
/// Set 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<Self>, timeout: Duration) {
let now = self.timer_manager.clock.read_time();
let expired_time = now + 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 mut timer_callback = self.timer_callback.lock_irq_disabled();
if let Some(timer_callback) = timer_callback.upgrade() {
timer_callback.cancel();
}
*timer_callback = Arc::downgrade(&new_timer_callback);
self.timer_manager.insert(new_timer_callback);
}
/// Return the current expired time of this timer.
pub fn expired_time(&self) -> Duration {
let timer_callback = self.timer_callback.lock_irq_disabled().upgrade();
timer_callback.map_or(Duration::ZERO, |timer_callback| timer_callback.expired_time)
}
/// Return 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 expired_time = self.expired_time();
if expired_time > now {
expired_time - now
} else {
Duration::ZERO
}
}
/// Return a reference to the [`TimerManager`] which manages
/// the current timer.
pub fn timer_manager(&self) -> &Arc<TimerManager> {
&self.timer_manager
}
}
fn interval_timer_callback(timer: &Weak<Timer>) {
let Some(timer) = timer.upgrade() else {
return;
};
(timer.registered_callback)();
let interval = timer.interval.lock_irq_disabled();
if *interval != Duration::ZERO {
timer.set_timeout(*interval);
}
}
/// `TimerManager` is used to create timers and manage their expiries. It holds a clock and can
/// create [`Timer`]s based on this clock.
///
/// These created `Timer`s will hold an `Arc` pointer to this manager, hence this manager
/// will be actually dropped after all the created timers have been dropped.
pub struct TimerManager {
clock: Arc<dyn Clock>,
timer_callbacks: SpinLock<BinaryHeap<Arc<TimerCallback>>>,
}
impl TimerManager {
/// Create a `TimerManager` instance from a clock.
pub fn new(clock: Arc<dyn Clock>) -> Arc<Self> {
Arc::new(Self {
clock,
timer_callbacks: SpinLock::new(BinaryHeap::new()),
})
}
fn insert(&self, timer_callback: Arc<TimerCallback>) {
self.timer_callbacks
.lock_irq_disabled()
.push(timer_callback);
}
/// Check the managed timers, and if any have timed out,
/// call the corresponding callback functions.
pub fn process_expired_timers(&self) {
let callbacks = {
let mut timeout_list = self.timer_callbacks.lock_irq_disabled();
if timeout_list.len() == 0 {
return;
}
let mut callbacks = Vec::new();
let current_time = self.clock.read_time();
while let Some(t) = timeout_list.peek() {
if t.is_cancelled() {
// Just ignore the cancelled callback
timeout_list.pop();
} else if t.expired_time <= current_time {
callbacks.push(timeout_list.pop().unwrap());
} else {
break;
}
}
callbacks
};
for callback in callbacks {
(callback.callback)();
}
}
/// Create an [`Timer`], which will be managed by this `TimerManager`.
pub fn create_timer<F>(self: &Arc<Self>, function: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
Timer::new(function, self.clone())
}
}
/// A `TimerCallback` can be used to execute a timer callback function.
struct TimerCallback {
expired_time: Duration,
callback: Box<dyn Fn() + Send + Sync>,
is_cancelled: AtomicBool,
}
impl TimerCallback {
/// Create an instance of `TimerCallback`.
fn new(timeout: Duration, callback: Box<dyn Fn() + Send + Sync>) -> Self {
Self {
expired_time: timeout,
callback,
is_cancelled: AtomicBool::new(false),
}
}
/// Cancel 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.
fn is_cancelled(&self) -> bool {
self.is_cancelled.load(Ordering::Acquire)
}
}
impl PartialEq for TimerCallback {
fn eq(&self, other: &Self) -> bool {
self.expired_time == other.expired_time
}
}
impl Eq for TimerCallback {}
impl PartialOrd for TimerCallback {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TimerCallback {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
// We want `TimerCallback`s to be processed in ascending order of `expired_time`,
// and the in-order management of `TimerCallback`s currently relies on a maximum heap,
// so we need the reverse instruction here.
self.expired_time.cmp(&other.expired_time).reverse()
}
}

View File

@ -1,15 +1,19 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use core::time::Duration;
use aster_time::read_monotonic_time; pub use core::{timer, Clock};
use ::core::time::Duration;
pub use system_time::{SystemTime, START_TIME};
pub use timer::{Timer, TimerManager};
use crate::prelude::*; use crate::prelude::*;
pub mod clocks;
mod core;
mod system_time; mod system_time;
pub mod wait;
pub use system_time::{SystemTime, START_TIME};
pub type clockid_t = i32; pub type clockid_t = i32;
pub type time_t = i64; pub type time_t = i64;
@ -17,32 +21,10 @@ pub type suseconds_t = i64;
pub type clock_t = i64; pub type clock_t = i64;
pub(super) fn init() { pub(super) fn init() {
system_time::init_start_time(); system_time::init();
clocks::init();
} }
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
#[repr(i32)]
pub enum ClockID {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
CLOCK_PROCESS_CPUTIME_ID = 2,
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
/// A list of all supported clock IDs for time-related functions.
pub const ALL_SUPPORTED_CLOCK_IDS: [ClockID; 6] = [
ClockID::CLOCK_REALTIME,
ClockID::CLOCK_REALTIME_COARSE,
ClockID::CLOCK_MONOTONIC,
ClockID::CLOCK_MONOTONIC_COARSE,
ClockID::CLOCK_MONOTONIC_RAW,
ClockID::CLOCK_BOOTTIME,
];
#[repr(C)] #[repr(C)]
#[derive(Debug, Default, Copy, Clone, Pod)] #[derive(Debug, Default, Copy, Clone, Pod)]
pub struct timespec_t { pub struct timespec_t {
@ -90,27 +72,6 @@ impl From<timeval_t> for Duration {
/// The various flags for setting POSIX.1b interval timers: /// The various flags for setting POSIX.1b interval timers:
pub const TIMER_ABSTIME: i32 = 0x01; pub const TIMER_ABSTIME: i32 = 0x01;
pub fn now_as_duration(clock_id: &ClockID) -> Result<Duration> {
match clock_id {
ClockID::CLOCK_MONOTONIC
| ClockID::CLOCK_MONOTONIC_COARSE
| ClockID::CLOCK_MONOTONIC_RAW
| ClockID::CLOCK_BOOTTIME => Ok(read_monotonic_time()),
ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => {
let now = SystemTime::now();
now.duration_since(&SystemTime::UNIX_EPOCH)
}
_ => {
warn!(
"unsupported clock_id: {:?}, treat it as CLOCK_REALTIME",
clock_id
);
let now = SystemTime::now();
now.duration_since(&SystemTime::UNIX_EPOCH)
}
}
}
/// Unix time measures time by the number of seconds that have elapsed since /// Unix time measures time by the number of seconds that have elapsed since
/// the Unix epoch, without adjustments made due to leap seconds. /// the Unix epoch, without adjustments made due to leap seconds.
#[repr(C)] #[repr(C)]

View File

@ -13,9 +13,12 @@ use crate::prelude::*;
pub struct SystemTime(PrimitiveDateTime); pub struct SystemTime(PrimitiveDateTime);
pub static START_TIME: Once<SystemTime> = Once::new(); pub static START_TIME: Once<SystemTime> = Once::new();
pub(super) static START_TIME_AS_DURATION: Once<Duration> = Once::new();
pub(super) fn init_start_time() { pub(super) fn init() {
let start_time = convert_system_time(read_start_time()).unwrap(); let start_time = convert_system_time(read_start_time()).unwrap();
START_TIME_AS_DURATION
.call_once(|| start_time.duration_since(&SystemTime::UNIX_EPOCH).unwrap());
START_TIME.call_once(|| start_time); START_TIME.call_once(|| start_time);
} }
@ -37,6 +40,7 @@ impl SystemTime {
/// Returns the current system time /// Returns the current system time
pub fn now() -> Self { pub fn now() -> Self {
// The get real time result should always be valid
START_TIME START_TIME
.get() .get()
.unwrap() .unwrap()

View File

@ -16,7 +16,6 @@ use core::time::Duration;
use aster_frame::{ use aster_frame::{
sync::SpinLock, sync::SpinLock,
timer::Timer,
vm::{VmFrame, VmIo, PAGE_SIZE}, vm::{VmFrame, VmIo, PAGE_SIZE},
}; };
use aster_rights::Rights; use aster_rights::Rights;
@ -27,7 +26,8 @@ use spin::Once;
use crate::{ use crate::{
fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD}, fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD},
time::{ClockID, SystemTime, START_TIME}, syscall::ClockID,
time::{clocks::MonotonicClock, SystemTime, START_TIME},
vm::vmo::{Vmo, VmoOptions}, vm::vmo::{Vmo, VmoOptions},
}; };
@ -291,11 +291,9 @@ fn update_vdso_high_res_instant(instant: Instant, instant_cycles: u64) {
} }
/// Update the `VdsoInstant` for clock IDs with coarse resolution in Vdso. /// Update the `VdsoInstant` for clock IDs with coarse resolution in Vdso.
fn update_vdso_coarse_res_instant(timer: Arc<Timer>) { fn update_vdso_coarse_res_instant() {
let instant = Instant::from(read_monotonic_time()); let instant = Instant::from(read_monotonic_time());
VDSO.get().unwrap().update_coarse_res_instant(instant); VDSO.get().unwrap().update_coarse_res_instant(instant);
timer.set(Duration::from_millis(100));
} }
/// Init `START_SECS_COUNT`, which is used to record the seconds passed since 1970-01-01 00:00:00. /// Init `START_SECS_COUNT`, which is used to record the seconds passed since 1970-01-01 00:00:00.
@ -321,8 +319,10 @@ pub(super) fn init() {
// Coarse resolution clock IDs directly read the instant stored in VDSO data without // Coarse resolution clock IDs directly read the instant stored in VDSO data without
// using coefficients for calculation, thus the related instant requires more frequent updating. // using coefficients for calculation, thus the related instant requires more frequent updating.
let coarse_instant_timer = Timer::new(update_vdso_coarse_res_instant).unwrap(); let coarse_instant_timer =
coarse_instant_timer.set(Duration::from_millis(100)); MonotonicClock::timer_manager().create_timer(update_vdso_coarse_res_instant);
coarse_instant_timer.set_interval(Duration::from_millis(100));
coarse_instant_timer.set_timeout(Duration::from_millis(100));
} }
/// Return the VDSO vmo. /// Return the VDSO vmo.