ax_plat/time.rs
1//! Time-related operations.
2
3pub use core::time::Duration;
4
5/// A measurement of the system clock.
6///
7/// Currently, it reuses the [`core::time::Duration`] type. But it does not
8/// represent a duration, but a clock time.
9pub type TimeValue = Duration;
10
11/// Number of milliseconds in a second.
12pub const MILLIS_PER_SEC: u64 = 1_000;
13/// Number of microseconds in a second.
14pub const MICROS_PER_SEC: u64 = 1_000_000;
15/// Number of nanoseconds in a second.
16pub const NANOS_PER_SEC: u64 = 1_000_000_000;
17/// Number of nanoseconds in a millisecond.
18pub const NANOS_PER_MILLIS: u64 = 1_000_000;
19/// Number of nanoseconds in a microsecond.
20pub const NANOS_PER_MICROS: u64 = 1_000;
21
22/// Platform assessment of the raw counter used by the scheduler clock.
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum SchedulerClockStability {
25 /// Every CPU observes one synchronized system counter.
26 Stable,
27 /// The raw counter is CPU-local and requires per-CPU correction.
28 Unstable,
29}
30
31/// Failure to access the platform scheduler clock lifecycle.
32#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
33pub enum SchedulerClockError {
34 /// The logical CPU index is outside the installed per-CPU layout.
35 #[error("logical CPU {cpu_id} is outside the installed per-CPU layout")]
36 InvalidCpu { cpu_id: usize },
37 /// The calling CPU has no validated CPU-local area yet.
38 #[error("the calling CPU has no validated CPU-local area")]
39 CurrentCpuUnavailable,
40 /// An owner-only lifecycle operation was invoked from another CPU.
41 #[error("scheduler clock CPU mismatch: expected {expected_cpu_id}, current {actual_cpu_id}")]
42 WrongCurrentCpu {
43 expected_cpu_id: usize,
44 actual_cpu_id: usize,
45 },
46 /// The CPU scheduler clock is already online or being initialized.
47 #[error("the scheduler clock CPU is already online")]
48 CpuAlreadyOnline,
49 /// The CPU scheduler clock is offline.
50 #[error("the scheduler clock CPU is offline")]
51 CpuOffline,
52}
53
54/// Time-related interfaces.
55#[def_plat_interface]
56pub trait TimeIf {
57 /// Returns the current clock time in hardware ticks.
58 fn current_ticks() -> u64;
59
60 /// Converts hardware ticks to nanoseconds.
61 fn ticks_to_nanos(ticks: u64) -> u64;
62
63 /// Converts nanoseconds to hardware ticks.
64 fn nanos_to_ticks(nanos: u64) -> u64;
65
66 /// Reports whether the current architecture counter is synchronized
67 /// across every runtime CPU.
68 fn scheduler_clock_stability() -> SchedulerClockStability;
69
70 /// Return epoch offset in nanoseconds (wall time offset to monotonic
71 /// clock start).
72 fn epochoffset_nanos() -> u64;
73
74 /// Returns the IRQ number for the timer interrupt.
75 fn irq_num() -> irq_framework::IrqId;
76
77 /// Set a one-shot timer.
78 ///
79 /// A timer interrupt will be triggered at the specified monotonic time
80 /// deadline (in nanoseconds).
81 fn set_oneshot_timer(deadline_ns: u64);
82}
83
84/// Initializes the current CPU's scheduler-clock anchor before scheduler use.
85///
86/// # Errors
87///
88/// Returns an error if `cpu_id` does not identify the current installed CPU
89/// area or if that CPU clock is already online.
90///
91/// # Safety
92///
93/// The current CPU must be offline, non-migrating and unable to take an
94/// interrupt that can access scheduler-clock state.
95pub unsafe fn init_scheduler_clock(cpu_id: usize) -> Result<(), SchedulerClockError> {
96 let stability = scheduler_clock_stability();
97 let raw_clock = ticks_to_nanos(current_ticks());
98 // SAFETY: forwarded from this function's offline-CPU contract.
99 unsafe { crate::scheduler_clock::online_current_cpu(cpu_id, raw_clock, stability) }
100}
101
102/// Stops the current CPU's scheduler-clock publication.
103///
104/// # Errors
105///
106/// Returns an error if `cpu_id` is not current or its clock is already offline.
107///
108/// # Safety
109///
110/// The scheduler must have closed remote admission to this CPU and the caller
111/// must exclude migration, local IRQs and scheduler-clock re-entry.
112pub unsafe fn shutdown_scheduler_clock(cpu_id: usize) -> Result<(), SchedulerClockError> {
113 // SAFETY: forwarded from this function's scheduler lifecycle contract.
114 unsafe { crate::scheduler_clock::offline_current_cpu(cpu_id) }
115}
116
117/// Samples `cpu_id`'s comparable wrapping scheduler clock in nanoseconds.
118///
119/// Stable platforms use the calling CPU's synchronized system counter.
120/// Unstable platforms update the calling CPU's local publication, then couple
121/// it atomically with the target publication without reading the target raw
122/// counter.
123///
124/// # Errors
125///
126/// Returns an error when the target or calling CPU clock is offline, or when
127/// `cpu_id` is outside the installed CPU-local layout.
128///
129/// # Safety
130///
131/// The caller must prevent migration for the complete operation. Scheduler
132/// callers normally satisfy this through the target runqueue IRQ-save lock.
133#[inline]
134pub unsafe fn scheduler_clock_source(cpu_id: usize) -> Result<u64, SchedulerClockError> {
135 let raw_clock = ticks_to_nanos(current_ticks());
136 // SAFETY: forwarded from this function's migration-exclusion contract.
137 unsafe { crate::scheduler_clock::source(cpu_id, raw_clock) }
138}
139
140/// Stamps the current CPU's scheduler clock from a local timer interrupt.
141///
142/// The stability assessment is refreshed here so a late x86 TSC adjustment
143/// can move the owner from the direct fast path to corrected per-CPU clocks
144/// without a discontinuity.
145///
146/// # Errors
147///
148/// Returns an error if the current CPU clock has not been initialized.
149///
150/// # Safety
151///
152/// The caller must exclude migration and local scheduler-clock re-entry. The
153/// local timer interrupt path naturally satisfies both conditions.
154#[inline]
155pub unsafe fn scheduler_clock_tick() -> Result<u64, SchedulerClockError> {
156 let stability = scheduler_clock_stability();
157 let raw_clock = ticks_to_nanos(current_ticks());
158 // SAFETY: forwarded from this function's local tick contract.
159 unsafe { crate::scheduler_clock::tick(raw_clock, stability) }
160}
161
162/// Returns nanoseconds elapsed since system boot.
163pub fn monotonic_time_nanos() -> u64 {
164 ticks_to_nanos(current_ticks())
165}
166
167/// Returns the time elapsed since system boot in [`TimeValue`].
168pub fn monotonic_time() -> TimeValue {
169 TimeValue::from_nanos(monotonic_time_nanos())
170}
171
172/// Returns nanoseconds elapsed since epoch (also known as realtime).
173pub fn wall_time_nanos() -> u64 {
174 monotonic_time_nanos() + epochoffset_nanos()
175}
176
177/// Returns the time elapsed since epoch (also known as realtime) in [`TimeValue`].
178pub fn wall_time() -> TimeValue {
179 TimeValue::from_nanos(monotonic_time_nanos() + epochoffset_nanos())
180}
181
182/// Busy waiting for the given duration.
183pub fn busy_wait(dur: Duration) {
184 busy_wait_until(monotonic_time() + dur);
185}
186
187/// Busy waiting until reaching the given monotonic deadline.
188pub fn busy_wait_until(deadline: TimeValue) {
189 while monotonic_time() < deadline {
190 core::hint::spin_loop();
191 }
192}