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 #[cfg(feature = "irq")]
76 fn irq_num() -> irq_framework::IrqId;
77
78 /// Set a one-shot timer.
79 ///
80 /// A timer interrupt will be triggered at the specified monotonic time
81 /// deadline (in nanoseconds).
82 #[cfg(feature = "irq")]
83 fn set_oneshot_timer(deadline_ns: u64);
84}
85
86/// Initializes the current CPU's scheduler-clock anchor before scheduler use.
87///
88/// # Errors
89///
90/// Returns an error if `cpu_id` does not identify the current installed CPU
91/// area or if that CPU clock is already online.
92///
93/// # Safety
94///
95/// The current CPU must be offline, non-migrating and unable to take an
96/// interrupt that can access scheduler-clock state.
97pub unsafe fn init_scheduler_clock(cpu_id: usize) -> Result<(), SchedulerClockError> {
98 let stability = scheduler_clock_stability();
99 let raw_clock = ticks_to_nanos(current_ticks());
100 // SAFETY: forwarded from this function's offline-CPU contract.
101 unsafe { crate::scheduler_clock::online_current_cpu(cpu_id, raw_clock, stability) }
102}
103
104/// Stops the current CPU's scheduler-clock publication.
105///
106/// # Errors
107///
108/// Returns an error if `cpu_id` is not current or its clock is already offline.
109///
110/// # Safety
111///
112/// The scheduler must have closed remote admission to this CPU and the caller
113/// must exclude migration, local IRQs and scheduler-clock re-entry.
114pub unsafe fn shutdown_scheduler_clock(cpu_id: usize) -> Result<(), SchedulerClockError> {
115 // SAFETY: forwarded from this function's scheduler lifecycle contract.
116 unsafe { crate::scheduler_clock::offline_current_cpu(cpu_id) }
117}
118
119/// Samples `cpu_id`'s comparable wrapping scheduler clock in nanoseconds.
120///
121/// Stable platforms use the calling CPU's synchronized system counter.
122/// Unstable platforms update the calling CPU's local publication, then couple
123/// it atomically with the target publication without reading the target raw
124/// counter.
125///
126/// # Errors
127///
128/// Returns an error when the target or calling CPU clock is offline, or when
129/// `cpu_id` is outside the installed CPU-local layout.
130///
131/// # Safety
132///
133/// The caller must prevent migration for the complete operation. Scheduler
134/// callers normally satisfy this through the target runqueue IRQ-save lock.
135#[inline]
136pub unsafe fn scheduler_clock_source(cpu_id: usize) -> Result<u64, SchedulerClockError> {
137 let raw_clock = ticks_to_nanos(current_ticks());
138 // SAFETY: forwarded from this function's migration-exclusion contract.
139 unsafe { crate::scheduler_clock::source(cpu_id, raw_clock) }
140}
141
142/// Stamps the current CPU's scheduler clock from a local timer interrupt.
143///
144/// The stability assessment is refreshed here so a late x86 TSC adjustment
145/// can move the owner from the direct fast path to corrected per-CPU clocks
146/// without a discontinuity.
147///
148/// # Errors
149///
150/// Returns an error if the current CPU clock has not been initialized.
151///
152/// # Safety
153///
154/// The caller must exclude migration and local scheduler-clock re-entry. The
155/// local timer interrupt path naturally satisfies both conditions.
156#[inline]
157pub unsafe fn scheduler_clock_tick() -> Result<u64, SchedulerClockError> {
158 let stability = scheduler_clock_stability();
159 let raw_clock = ticks_to_nanos(current_ticks());
160 // SAFETY: forwarded from this function's local tick contract.
161 unsafe { crate::scheduler_clock::tick(raw_clock, stability) }
162}
163
164/// Returns nanoseconds elapsed since system boot.
165pub fn monotonic_time_nanos() -> u64 {
166 ticks_to_nanos(current_ticks())
167}
168
169/// Returns the time elapsed since system boot in [`TimeValue`].
170pub fn monotonic_time() -> TimeValue {
171 TimeValue::from_nanos(monotonic_time_nanos())
172}
173
174/// Returns nanoseconds elapsed since epoch (also known as realtime).
175pub fn wall_time_nanos() -> u64 {
176 monotonic_time_nanos() + epochoffset_nanos()
177}
178
179/// Returns the time elapsed since epoch (also known as realtime) in [`TimeValue`].
180pub fn wall_time() -> TimeValue {
181 TimeValue::from_nanos(monotonic_time_nanos() + epochoffset_nanos())
182}
183
184/// Busy waiting for the given duration.
185pub fn busy_wait(dur: Duration) {
186 busy_wait_until(monotonic_time() + dur);
187}
188
189/// Busy waiting until reaching the given monotonic deadline.
190pub fn busy_wait_until(deadline: TimeValue) {
191 while monotonic_time() < deadline {
192 core::hint::spin_loop();
193 }
194}