1use alloc::{boxed::Box, format, vec::Vec};
4use core::{
5 sync::atomic::{AtomicBool, AtomicU64, Ordering},
6 task::{Context, Poll},
7};
8
9use ax_hal::time::{TimeValue, monotonic_time};
10use ax_timer_list::{TimerEvent, TimerList};
11
12#[cfg(feature = "smp")]
13use crate::select_run_queue;
14use crate::{
15 AxCpuMask, AxTaskRef, IrqNotify, TaskInner, current_run_queue,
16 future::time::{FutureTimerHandle, TimerRuntime},
17 sync::{PreemptIrqSaveGuard, RawState, SpinLock},
18};
19
20mod clock_event;
21mod kernel;
22
23pub use clock_event::ClockEventControl;
24pub use kernel::{
25 HardKernelTimerAction, HardKernelTimerCallback, KernelTimerAction, KernelTimerCallback,
26 KernelTimerCancelOutcome, KernelTimerError, KernelTimerHandle, MonotonicDeadline,
27 MonotonicInstant, RestartableKernelTimerCallback, TimerCpuId,
28};
29use kernel::{KernelTimerEntry, KernelTimerQueue, KernelTimerQueueCancel};
30
31const KERNEL_TIMER_CAPACITY: usize = 1024;
32const TIMER_IRQ_BUDGET: usize = 64;
33
34static TIMER_TICKET_ID: AtomicU64 = AtomicU64::new(1);
35
36percpu_static! {
37 TIMER_BASE: SpinLock<PerCpuTimerBase> = SpinLock::new(PerCpuTimerBase::new()),
38 TIMER_NOTIFY: IrqNotify = IrqNotify::new(),
39 TIMER_WORKER_STARTED: AtomicBool = AtomicBool::new(false),
40 TIMER_CALLBACKS: Vec<Box<dyn Fn(TimeValue) + Send + Sync>> = Vec::new(),
41}
42
43struct PerCpuTimerBase {
49 task_wakeups: TimerList<TaskWakeupEvent>,
50 future_wakeups: TimerRuntime,
51 kernel_timers: KernelTimerQueue,
52}
53
54impl PerCpuTimerBase {
55 const fn new() -> Self {
56 Self {
57 task_wakeups: TimerList::new(),
58 future_wakeups: TimerRuntime::new(),
59 kernel_timers: KernelTimerQueue::new(KERNEL_TIMER_CAPACITY),
60 }
61 }
62
63 fn next_deadline(&self) -> Option<TimeValue> {
64 [
65 self.task_wakeups.next_deadline(),
66 self.future_wakeups.next_deadline(),
67 self.kernel_timers
68 .next_soft_deadline()
69 .map(MonotonicDeadline::as_duration),
70 self.kernel_timers
71 .next_hard_deadline()
72 .map(MonotonicDeadline::as_duration),
73 ]
74 .into_iter()
75 .flatten()
76 .min()
77 }
78}
79
80struct TaskWakeupEvent {
81 ticket_id: u64,
82 task: AxTaskRef,
83}
84
85impl TimerEvent for TaskWakeupEvent {
86 fn callback(self, _now: TimeValue) {
87 if self.task.timer_ticket() != self.ticket_id {
91 return;
94 }
95
96 wake_task_from_timer(self.task)
101 }
102}
103
104#[cfg(feature = "smp")]
105fn wake_task_from_timer(task: AxTaskRef) {
106 if task.cpumask().get(ax_hal::percpu::this_cpu_id()) {
107 current_run_queue::<RawState>().unblock_task(task, true);
108 } else {
109 select_run_queue::<RawState>(&task).unblock_task(task, true);
110 }
111}
112
113#[cfg(not(feature = "smp"))]
114fn wake_task_from_timer(task: AxTaskRef) {
115 current_run_queue::<RawState>().unblock_task(task, true);
116}
117
118pub fn register_timer_callback<F>(callback: F)
120where
121 F: Fn(TimeValue) + Send + Sync + 'static,
122{
123 with_local_exclusive(|exclusive| {
124 TIMER_CALLBACKS.with_current_mut(exclusive, |callbacks| callbacks.push(Box::new(callback)))
125 });
126}
127
128fn check_callbacks() {
129 with_local_pin(|pin| {
130 TIMER_CALLBACKS.with_current(pin, |callbacks| {
131 for callback in callbacks {
132 callback(monotonic_time());
133 }
134 })
135 });
136}
137
138fn deadline_to_nanos(deadline: TimeValue) -> u64 {
139 deadline.as_nanos().min(u64::MAX as u128) as u64
140}
141
142pub(crate) fn maybe_reprogram_timer(deadline: TimeValue) {
143 clock_event::publish_earlier_deadline(deadline_to_nanos(deadline));
144}
145
146pub(crate) fn next_deadline_nanos() -> Option<u64> {
147 with_current_timer_base(|timer_base| timer_base.next_deadline().map(deadline_to_nanos))
148}
149
150pub(crate) fn set_alarm_wakeup(deadline: TimeValue, task: AxTaskRef) {
151 let _owner_guard = PreemptIrqSaveGuard::new();
152 with_current_timer_base(|timer_base| {
153 let ticket_id = TIMER_TICKET_ID.fetch_add(1, Ordering::AcqRel);
154 task.set_timer_ticket(ticket_id);
155 timer_base
156 .task_wakeups
157 .set(deadline, TaskWakeupEvent { ticket_id, task });
158 });
159 maybe_reprogram_timer(deadline);
160}
161
162pub(crate) fn register_future_timer(deadline: TimeValue) -> Option<FutureTimerHandle> {
163 let _owner_guard = PreemptIrqSaveGuard::new();
164 let owner_cpu = ax_hal::percpu::this_cpu_id();
165 let key = with_timer_base(owner_cpu, |timer_base| {
166 timer_base.future_wakeups.add(deadline)
167 })?;
168 maybe_reprogram_timer(deadline);
169 Some(FutureTimerHandle::new(owner_cpu, key))
170}
171
172pub(crate) fn poll_future_timer(handle: FutureTimerHandle, context: &mut Context<'_>) -> Poll<()> {
173 with_timer_base(handle.owner_cpu(), |timer_base| {
174 timer_base.future_wakeups.poll(&handle.key(), context)
175 })
176}
177
178pub(crate) fn cancel_future_timer(handle: FutureTimerHandle) {
179 with_timer_base(handle.owner_cpu(), |timer_base| {
180 timer_base.future_wakeups.cancel(&handle.key())
181 });
182}
183
184pub fn register_kernel_timer(
189 deadline: MonotonicDeadline,
190 callback: KernelTimerCallback,
191) -> Result<KernelTimerHandle, KernelTimerError> {
192 validate_kernel_timer_context()?;
193 register_kernel_timer_entry(KernelTimerEntry::new(deadline, callback)?)
194}
195
196pub fn register_restartable_kernel_timer(
198 deadline: MonotonicDeadline,
199 callback: RestartableKernelTimerCallback,
200) -> Result<KernelTimerHandle, KernelTimerError> {
201 validate_kernel_timer_context()?;
202 register_kernel_timer_entry(KernelTimerEntry::new_restartable(deadline, callback)?)
203}
204
205pub fn register_hard_restartable_kernel_timer(
207 deadline: MonotonicDeadline,
208 callback: HardKernelTimerCallback,
209) -> Result<KernelTimerHandle, KernelTimerError> {
210 validate_kernel_timer_context()?;
211 register_kernel_timer_entry(KernelTimerEntry::new_hard_restartable(deadline, callback)?)
212}
213
214pub fn arm_hard_kernel_timer(
216 handle: KernelTimerHandle,
217 deadline: MonotonicDeadline,
218) -> Result<(), KernelTimerError> {
219 validate_kernel_timer_context()?;
220 let _owner_guard = PreemptIrqSaveGuard::new();
221 let current_cpu = ax_hal::percpu::this_cpu_id();
222 if handle.owner().as_usize() != current_cpu {
223 return Err(KernelTimerError::OwnerMismatch {
224 expected: handle.owner().as_usize(),
225 actual: current_cpu,
226 });
227 }
228 let armed = try_with_timer_base(current_cpu, |timer_base| {
229 timer_base.kernel_timers.arm_hard(handle, deadline)
230 })?;
231 if !armed {
232 return Err(KernelTimerError::StaleHandle);
233 }
234 maybe_reprogram_timer(deadline.as_duration());
235 Ok(())
236}
237
238pub fn disarm_hard_kernel_timer(handle: KernelTimerHandle) -> Result<(), KernelTimerError> {
240 validate_kernel_timer_context()?;
241 let owner_cpu = handle.owner().as_usize();
242 let found = try_with_timer_base(owner_cpu, |timer_base| {
243 timer_base.kernel_timers.disarm_hard(handle).is_some()
244 })?;
245 found.then_some(()).ok_or(KernelTimerError::StaleHandle)
246}
247
248pub fn cancel_kernel_timer(
253 handle: KernelTimerHandle,
254) -> Result<KernelTimerCancelOutcome, KernelTimerError> {
255 validate_kernel_timer_context()?;
256 let owner_cpu = handle.owner().as_usize();
257 let outcome = try_with_timer_base(owner_cpu, |timer_base| {
258 timer_base.kernel_timers.cancel(handle)
259 })?;
260 match outcome {
261 KernelTimerQueueCancel::Cancelled(entry) => {
262 drop(entry);
263 Ok(KernelTimerCancelOutcome::Cancelled)
264 }
265 KernelTimerQueueCancel::Executing => Ok(KernelTimerCancelOutcome::NotCancelled),
266 KernelTimerQueueCancel::Stale => Err(KernelTimerError::StaleHandle),
267 }
268}
269
270fn register_kernel_timer_entry(
271 entry: KernelTimerEntry,
272) -> Result<KernelTimerHandle, KernelTimerError> {
273 let _owner_guard = PreemptIrqSaveGuard::new();
274 let owner_cpu = ax_hal::percpu::this_cpu_id();
275 let deadline = entry
276 .deadline_for_registration()
277 .expect("new kernel timer must start armed");
278 let result = try_with_timer_base(owner_cpu, |timer_base| {
279 timer_base
280 .kernel_timers
281 .insert(TimerCpuId::new(owner_cpu), entry)
282 })?;
283 let handle = result.map_err(|_entry| KernelTimerError::Capacity { cpu_id: owner_cpu })?;
284 maybe_reprogram_timer(deadline.as_duration());
285 Ok(handle)
286}
287
288#[derive(Clone, Copy, Debug, Eq, PartialEq)]
289enum KernelTimerCallContext {
290 HardIrq,
291 Thread,
292 ThreadCriticalSection,
293}
294
295fn current_kernel_timer_call_context() -> KernelTimerCallContext {
296 if ax_hal::irq::in_irq_context() {
297 KernelTimerCallContext::HardIrq
298 } else if crate::in_atomic_context() {
299 KernelTimerCallContext::ThreadCriticalSection
300 } else {
301 KernelTimerCallContext::Thread
302 }
303}
304
305fn validate_kernel_timer_call_context(
306 context: KernelTimerCallContext,
307) -> Result<(), KernelTimerError> {
308 if context == KernelTimerCallContext::HardIrq {
309 Err(KernelTimerError::UnsafeContext)
310 } else {
311 Ok(())
312 }
313}
314
315fn validate_kernel_timer_context() -> Result<(), KernelTimerError> {
316 validate_kernel_timer_call_context(current_kernel_timer_call_context())
317}
318
319pub fn check_events(run_callbacks: bool) {
322 if run_callbacks {
323 check_callbacks();
324 }
325 let mut remaining_budget = TIMER_IRQ_BUDGET;
326 let hard_now = MonotonicInstant::from_duration(monotonic_time())
327 .expect("host monotonic clock must remain finite");
328 let mut hard_completed = false;
329 let mut hard_expired = 0;
330 while remaining_budget != 0 {
331 let execution =
332 with_current_timer_base(|timer_base| timer_base.kernel_timers.claim_due_hard(hard_now));
333 let Some(mut execution) = execution else {
334 break;
335 };
336 let action = unsafe {
337 execution.invoke_hard()
341 };
342 hard_completed |= with_current_timer_base(|timer_base| {
343 timer_base
344 .kernel_timers
345 .complete_hard_execution(execution, action)
346 });
347 hard_expired += 1;
348 remaining_budget -= 1;
349 }
350
351 let mut task_expired = 0;
352 while remaining_budget != 0 {
353 let now = monotonic_time();
354 let event = with_current_timer_base(|timer_base| timer_base.task_wakeups.expire_one(now));
355 if let Some((_deadline, event)) = event {
356 event.callback(now);
357 task_expired += 1;
358 remaining_budget -= 1;
359 } else {
360 break;
361 }
362 }
363
364 let soft_now = MonotonicInstant::from_duration(monotonic_time())
365 .expect("host monotonic clock must remain finite");
366 let (soft_due, future_due, soft_promoted, soft_pending) =
367 with_current_timer_base(|timer_base| {
368 let future_due = timer_base
369 .future_wakeups
370 .publish_due_work(soft_now.as_duration());
371 let kernel = timer_base
372 .kernel_timers
373 .expire_due_soft(soft_now, remaining_budget);
374 let soft_due = future_due
375 || kernel.expired() != 0
376 || kernel.pending()
377 || timer_base.kernel_timers.has_expired()
378 || timer_base.kernel_timers.has_completed();
379 (soft_due, future_due, kernel.expired(), kernel.pending())
380 });
381 trace!(
382 "timer IRQ CPU {}: hard_expired={}, task_expired={}, soft_promoted={}, future_due={}, \
383 soft_pending={}, budget_left={}",
384 ax_hal::percpu::this_cpu_id(),
385 hard_expired,
386 task_expired,
387 soft_promoted,
388 future_due,
389 soft_pending,
390 remaining_budget
391 );
392 if soft_due || hard_completed {
393 current_timer_notify().notify_irq();
394 }
395}
396
397pub fn init_timer_service() {
402 let cpu_id = ax_hal::percpu::this_cpu_id();
403 if timer_worker_started(cpu_id)
404 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
405 .is_err()
406 {
407 return;
408 }
409
410 with_timer_base(cpu_id, |timer_base| {
411 timer_base
412 .kernel_timers
413 .reserve_transition_capacity(cpu_id)
414 .unwrap_or_else(|error| {
415 panic!("failed to reserve CPU {cpu_id} kernel timer queues: {error}")
416 });
417 });
418
419 let notify = timer_notify(cpu_id);
420 let worker = TaskInner::new(
421 move || loop {
422 notify.wait();
423 drain_soft_timer_events(cpu_id);
424 },
425 format!("ktimers/{cpu_id}"),
426 crate::default_task_stack_size(),
427 );
428 let mut affinity = AxCpuMask::new();
429 affinity.set(cpu_id, true);
430 worker.set_cpumask(affinity);
431 crate::spawn_task(worker);
432}
433
434fn drain_soft_timer_events(cpu_id: usize) {
435 let mut processed = 0;
436 while processed < TIMER_IRQ_BUDGET {
437 if let Some(waker) = with_timer_base(cpu_id, |timer_base| {
438 timer_base.future_wakeups.expire_one(monotonic_time())
439 }) {
440 waker.wake();
441 processed += 1;
442 continue;
443 }
444
445 if let Some(mut execution) = with_timer_base(cpu_id, |timer_base| {
446 timer_base.kernel_timers.claim_expired()
447 }) {
448 let action = execution.invoke_soft();
449 let completed = with_timer_base(cpu_id, |timer_base| {
450 timer_base
451 .kernel_timers
452 .complete_soft_execution(execution, action)
453 });
454 drop(completed);
455 processed += 1;
456 continue;
457 }
458
459 if let Some(completed) = with_timer_base(cpu_id, |timer_base| {
460 timer_base.kernel_timers.claim_completed()
461 }) {
462 drop(completed);
463 processed += 1;
464 continue;
465 }
466 break;
467 }
468
469 let now = monotonic_time();
470 let pending = with_timer_base(cpu_id, |timer_base| {
471 timer_base.future_wakeups.finish_due_work(now)
472 || timer_base.kernel_timers.has_expired()
473 || timer_base.kernel_timers.has_completed()
474 });
475 if pending {
476 timer_notify(cpu_id).notify();
477 crate::yield_now();
478 } else if cpu_id == ax_hal::percpu::this_cpu_id()
479 && let Some(deadline) = with_timer_base(cpu_id, |timer_base| timer_base.next_deadline())
480 {
481 maybe_reprogram_timer(deadline);
482 }
483}
484
485fn with_current_timer_base<R>(operation: impl FnOnce(&mut PerCpuTimerBase) -> R) -> R {
486 with_timer_base(ax_hal::percpu::this_cpu_id(), operation)
487}
488
489fn with_timer_base<R>(cpu_id: usize, operation: impl FnOnce(&mut PerCpuTimerBase) -> R) -> R {
490 operation(&mut timer_base(cpu_id).lock_irqsave())
491}
492
493fn try_with_timer_base<R>(
494 cpu_id: usize,
495 operation: impl FnOnce(&mut PerCpuTimerBase) -> R,
496) -> Result<R, KernelTimerError> {
497 let timer_base = try_timer_base(cpu_id)?;
498 Ok(operation(&mut timer_base.lock_irqsave()))
499}
500
501fn timer_base(cpu_id: usize) -> &'static SpinLock<PerCpuTimerBase> {
502 try_timer_base(cpu_id).unwrap_or_else(|error| panic!("timer-base access failed: {error}"))
503}
504
505fn try_timer_base(cpu_id: usize) -> Result<&'static SpinLock<PerCpuTimerBase>, KernelTimerError> {
506 let area = try_timer_cpu_area(cpu_id)?;
507 Ok(unsafe { TIMER_BASE.remote_ptr(area).as_ref() })
514}
515
516fn timer_notify(cpu_id: usize) -> &'static IrqNotify {
517 let area = timer_cpu_area(cpu_id);
518 unsafe { TIMER_NOTIFY.remote_ptr(area).as_ref() }
521}
522
523fn current_timer_notify() -> &'static IrqNotify {
524 timer_notify(ax_hal::percpu::this_cpu_id())
525}
526
527fn timer_worker_started(cpu_id: usize) -> &'static AtomicBool {
528 let area = timer_cpu_area(cpu_id);
529 unsafe { TIMER_WORKER_STARTED.remote_ptr(area).as_ref() }
532}
533
534fn timer_cpu_area(cpu_id: usize) -> ax_percpu::PerCpuArea {
535 try_timer_cpu_area(cpu_id)
536 .unwrap_or_else(|error| panic!("timer CPU area access failed: {error}"))
537}
538
539fn try_timer_cpu_area(cpu_id: usize) -> Result<ax_percpu::PerCpuArea, KernelTimerError> {
540 let cpu_index = ax_percpu::CpuIndex::try_from(cpu_id)
541 .map_err(|_| KernelTimerError::CpuUnavailable { cpu_id })?;
542 ax_percpu::area(cpu_index).map_err(|_| KernelTimerError::CpuUnavailable { cpu_id })
543}
544
545fn with_local_pin<R>(
546 operation: impl for<'scope> FnOnce(&ax_hal::percpu::CpuPin<'scope>) -> R,
547) -> R {
548 let _guard = PreemptIrqSaveGuard::new();
549 unsafe { ax_hal::percpu::with_cpu_pin(operation) }
551 .expect("timer access requires an installed CPU-local area")
552}
553
554fn with_local_exclusive<R>(
555 operation: impl for<'exclusive> FnOnce(&ax_hal::percpu::ExclusiveCpu<'exclusive>) -> R,
556) -> R {
557 let _guard = PreemptIrqSaveGuard::new();
558 unsafe {
561 ax_hal::percpu::with_cpu_pin(|pin| ax_hal::percpu::with_exclusive_cpu(pin, operation))
562 }
563 .expect("timer access requires an installed CPU-local area")
564}
565
566#[cfg(test)]
567mod tests {
568 use super::{KernelTimerCallContext, validate_kernel_timer_call_context};
569
570 #[test]
571 fn vcpu_thread_critical_section_may_update_soft_timers() {
572 assert!(
573 validate_kernel_timer_call_context(KernelTimerCallContext::ThreadCriticalSection)
574 .is_ok()
575 );
576 }
577
578 #[test]
579 fn hard_irq_may_not_register_or_cancel_soft_timers() {
580 assert!(validate_kernel_timer_call_context(KernelTimerCallContext::HardIrq).is_err());
581 }
582}