Skip to main content

ax_runtime/thread/
scheduler_events.rs

1//! Physical scheduler timer and IPI event delivery.
2
3use core::sync::atomic::{AtomicU64, Ordering};
4
5const TASK_CLOCK_EVENT_IRQ_BUDGET: usize = 64;
6
7static TASK_TIMER_IRQ_COUNT: AtomicU64 = AtomicU64::new(0);
8#[cfg(feature = "qperf-metrics")]
9static SCHEDULER_IPI_SEND_COUNT: AtomicU64 = AtomicU64::new(0);
10#[cfg(feature = "qperf-metrics")]
11static SCHEDULER_IPI_CONSUME_COUNT: AtomicU64 = AtomicU64::new(0);
12#[cfg(feature = "qperf-metrics")]
13static IRQ_RETURN_SCHEDULER_CONTINUATION_COUNT: AtomicU64 = AtomicU64::new(0);
14#[cfg(feature = "qperf-metrics")]
15static IRQ_RETURN_SCHEDULER_WINDOW_COUNT: AtomicU64 = AtomicU64::new(0);
16
17/// Aggregate scheduler delivery counters for feature-gated qperf diagnostics.
18#[cfg(feature = "qperf-metrics")]
19#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
20pub struct QperfRuntimeSchedulerMetricsSnapshot {
21    pub task: ax_task::diagnostics::QperfSchedulerMetricsSnapshot,
22    pub active_mm_same_activations: u64,
23    pub active_mm_different_activations: u64,
24    pub active_mm_kernel_lazy_activations: u64,
25    pub active_mm_hardware_root_writes: u64,
26    pub active_mm_lease_activations: u64,
27    pub active_mm_lease_deactivations: u64,
28    pub active_mm_reclaim_ready: u64,
29    pub active_mm_reclaim_destroyed: u64,
30    pub scheduler_ipi_sends: u64,
31    pub scheduler_ipi_consumes: u64,
32    pub clockevent_irqs: u64,
33    pub irq_return_scheduler_continuations: u64,
34    pub irq_return_scheduler_windows: u64,
35}
36
37/// Returns the aggregate number of scheduler timer interrupts since boot.
38pub fn timer_irq_count() -> u64 {
39    TASK_TIMER_IRQ_COUNT.load(Ordering::Relaxed)
40}
41
42/// Returns aggregate task and physical-delivery counters without locking.
43#[cfg(feature = "qperf-metrics")]
44pub fn qperf_runtime_scheduler_metrics_snapshot() -> QperfRuntimeSchedulerMetricsSnapshot {
45    let active_mm = super::address_space::qperf_address_space_metrics_snapshot();
46    QperfRuntimeSchedulerMetricsSnapshot {
47        task: ax_task::diagnostics::qperf_scheduler_metrics_snapshot(),
48        active_mm_same_activations: active_mm.same_activations,
49        active_mm_different_activations: active_mm.different_activations,
50        active_mm_kernel_lazy_activations: active_mm.kernel_lazy_activations,
51        active_mm_hardware_root_writes: active_mm.hardware_root_writes,
52        active_mm_lease_activations: active_mm.lease_activations,
53        active_mm_lease_deactivations: active_mm.lease_deactivations,
54        active_mm_reclaim_ready: active_mm.reclaim_ready,
55        active_mm_reclaim_destroyed: active_mm.reclaim_destroyed,
56        scheduler_ipi_sends: SCHEDULER_IPI_SEND_COUNT.load(Ordering::Relaxed),
57        scheduler_ipi_consumes: SCHEDULER_IPI_CONSUME_COUNT.load(Ordering::Relaxed),
58        clockevent_irqs: timer_irq_count(),
59        irq_return_scheduler_continuations: IRQ_RETURN_SCHEDULER_CONTINUATION_COUNT
60            .load(Ordering::Relaxed),
61        irq_return_scheduler_windows: IRQ_RETURN_SCHEDULER_WINDOW_COUNT.load(Ordering::Relaxed),
62    }
63}
64
65#[cfg(feature = "qperf-metrics")]
66pub(crate) fn record_irq_return_scheduler_continuation() {
67    IRQ_RETURN_SCHEDULER_CONTINUATION_COUNT.fetch_add(1, Ordering::Relaxed);
68}
69
70#[cfg(feature = "qperf-metrics")]
71pub(crate) fn record_irq_return_scheduler_window() {
72    IRQ_RETURN_SCHEDULER_WINDOW_COUNT.fetch_add(1, Ordering::Relaxed);
73}
74
75#[cfg(all(feature = "qperf-metrics", any(feature = "ipi", feature = "wake-ipi")))]
76pub(crate) fn record_scheduler_ipi_send() {
77    SCHEDULER_IPI_SEND_COUNT.fetch_add(1, Ordering::Relaxed);
78}
79
80#[cfg(all(feature = "qperf-metrics", any(feature = "ipi", feature = "wake-ipi")))]
81pub(crate) fn record_scheduler_ipi_consume() {
82    SCHEDULER_IPI_CONSUME_COUNT.fetch_add(1, Ordering::Relaxed);
83}
84
85/// Performs bounded task accounting and publishes a sticky reschedule request.
86pub(crate) fn on_clock_event(
87    now: ax_task::time::MonotonicInstant,
88    scheduler_event: ax_task::runtime::service::ClaimedSchedulerDeadlines,
89) -> ax_task::runtime::service::TaskClockEventOutcome {
90    TASK_TIMER_IRQ_COUNT.fetch_add(1, Ordering::Relaxed);
91    account_clock_event(now, scheduler_event)
92}
93fn account_clock_event(
94    now: ax_task::time::MonotonicInstant,
95    scheduler_event: ax_task::runtime::service::ClaimedSchedulerDeadlines,
96) -> ax_task::runtime::service::TaskClockEventOutcome {
97    match ax_task::runtime::service::on_clock_event(
98        now,
99        TASK_CLOCK_EVENT_IRQ_BUDGET,
100        scheduler_event,
101    ) {
102        Ok(outcome) => outcome,
103        Err(error) => panic!("task clockevent accounting failed: {error}"),
104    }
105}
106pub(crate) fn publish_scheduler_tick(
107    stamp: ax_task::runtime::service::SchedulerTickStamp,
108    mode: ax_task::runtime::service::SchedulerTickMode,
109    tick_ns: u64,
110) {
111    ax_task::runtime::service::publish_scheduler_tick(stamp, mode, tick_ns)
112        .unwrap_or_else(|error| panic!("scheduler tick publication failed: {error}"));
113}