mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Add abstractions for Clock and Timer
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
0d5131c822
commit
d019de29f9
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -227,6 +227,7 @@ dependencies = [
|
|||||||
"libflate",
|
"libflate",
|
||||||
"log",
|
"log",
|
||||||
"lru",
|
"lru",
|
||||||
|
"paste",
|
||||||
"pod",
|
"pod",
|
||||||
"rand",
|
"rand",
|
||||||
"ringbuf",
|
"ringbuf",
|
||||||
|
@ -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
|
||||||
|
@ -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};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
framework/aster-frame/src/arch/x86/timer/jiffies.rs
Normal file
45
framework/aster-frame/src/arch/x86/timer/jiffies.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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};
|
||||||
|
@ -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, ×pec)?;
|
write_val_to_user(timespec_addr, ×pec)?;
|
||||||
|
|
||||||
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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::*;
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
9
kernel/aster-nix/src/time/clocks/mod.rs
Normal file
9
kernel/aster-nix/src/time/clocks/mod.rs
Normal 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();
|
||||||
|
}
|
294
kernel/aster-nix/src/time/clocks/system_wide.rs
Normal file
294
kernel/aster-nix/src/time/clocks/system_wide.rs
Normal 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))
|
||||||
|
});
|
||||||
|
}
|
26
kernel/aster-nix/src/time/core/mod.rs
Normal file
26
kernel/aster-nix/src/time/core/mod.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
237
kernel/aster-nix/src/time/core/timer.rs
Normal file
237
kernel/aster-nix/src/time/core/timer.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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)]
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
Reference in New Issue
Block a user