Skip to main content

ax_task/runtime/
cpu.rs

1//! Pinned CPU capabilities and scheduler observations.
2
3pub use crate::{
4    runtime::clock::{RqClockSample, SchedulerDeadlineUpdate, SchedulerRuntimeDeadline},
5    sched::system::{
6        CpuLifecycleState, CpuLoadSummary, CpuLocal, CpuLocalOwnerBorrow, CpuRemote, CpuSnapshot,
7    },
8};
9use crate::{
10    runtime::{
11        context::{current_cpu_remote, runtime_current_cpu, validate_schedule_context},
12        lock::PreemptScope,
13        switch::RuntimeScheduleOrigin,
14        task_runtime,
15    },
16    thread::TaskError,
17};
18
19/// Tests the current CPU's sticky reschedule request while migration is pinned.
20///
21/// # Safety
22///
23/// The caller must prevent migration until it has finished the decision that
24/// uses this snapshot. Sleeping-lock owner spinning normally satisfies this
25/// with a preemption guard.
26pub unsafe fn current_needs_reschedule_pinned() -> Result<bool, TaskError> {
27    Ok(current_cpu_remote()
28        .ok_or(TaskError::NotInitialized)?
29        .needs_reschedule())
30}
31
32/// Tests only scheduler work consumed by kernel preempt-enable/IRQ return.
33///
34/// # Safety
35///
36/// The caller must prevent migration until it has finished the decision that
37/// uses this snapshot.
38pub unsafe fn current_needs_immediate_scheduler_work_pinned() -> Result<bool, TaskError> {
39    Ok(current_cpu_remote()
40        .ok_or(TaskError::NotInitialized)?
41        .needs_immediate_scheduler_work())
42}
43
44/// Tests the sticky reschedule state of the calling CPU.
45pub fn current_cpu_needs_resched() -> Result<bool, TaskError> {
46    let _pin = PreemptScope::enter();
47    // SAFETY: `_pin` prevents migration through the remote reschedule-state
48    // observation. Stronger IRQ/scheduler owner scopes are inherited.
49    unsafe { current_needs_reschedule_pinned() }
50}
51
52/// Clears the current CPU's idle-polling state at the runtime sleep boundary.
53///
54/// # Safety
55///
56/// The runtime must have disabled local interrupts and must prevent migration
57/// through the immediately following sticky-work and clockevent recheck. This
58/// is Linux's `current_clr_polling_and_test()` boundary: work published before
59/// the clear is found by that recheck, while work published afterwards must
60/// own a physical interrupt edge.
61#[doc(hidden)]
62pub unsafe fn finish_current_cpu_idle_polling() -> Result<(), TaskError> {
63    let remote = current_cpu_remote().ok_or(TaskError::NotInitialized)?;
64    remote.finish_idle_wait();
65    Ok(())
66}
67
68/// Executes one lossless idle publication/recheck/WFI iteration.
69pub fn idle_current_cpu_once() -> Result<(), TaskError> {
70    validate_schedule_context(RuntimeScheduleOrigin::Preempt)?;
71    let may_wait = {
72        let cpu = runtime_current_cpu()?;
73        cpu.prepare_idle_wait()
74    };
75    if may_wait {
76        task_runtime::wait_for_interrupt();
77    }
78    Ok(())
79}
80use crate::runtime::handle::opaque_handle;
81
82opaque_handle!(
83    /// Opaque address of the current CPU's pinned owner-only scheduler object.
84    ///
85    /// Consumers must claim the corresponding [`crate::runtime::cpu::CpuRemote`] owner gate
86    /// before reconstructing any reference from this address.
87    CurrentCpuLocalHandle,
88    "runtime::cpu"
89);
90opaque_handle!(
91    /// Opaque pointer-sized handle to one Arc-backed remote CPU endpoint.
92    ///
93    /// Remote and owner-only CPU handles are intentionally not interchangeable:
94    ///
95    /// ```compile_fail
96    /// use ax_task::runtime::cpu::{CpuRemoteHandle, CurrentCpuLocalHandle};
97    ///
98    /// fn borrow_owner(_handle: CurrentCpuLocalHandle) {}
99    /// borrow_owner(CpuRemoteHandle::NONE);
100    /// ```
101    CpuRemoteHandle,
102    "runtime::cpu"
103);
104opaque_handle!(
105    /// Token returned by the nested IRQ guard service.
106    IrqGuardToken,
107    "runtime::cpu"
108);
109opaque_handle!(
110    /// Token returned by the nested task-preemption guard service.
111    PreemptGuardToken,
112    "runtime::cpu"
113);
114
115/// Runtime-defined raw local-IRQ state saved by a synchronization guard.
116///
117/// Unlike [`IrqGuardToken`], this value does not own a scheduler publication
118/// scope. It only transports the architecture interrupt state back to the
119/// runtime that produced it.
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121#[repr(transparent)]
122pub struct LocalIrqState(usize);
123
124impl LocalIrqState {
125    /// Creates a saved local-IRQ state at the runtime provider boundary.
126    ///
127    /// # Safety
128    ///
129    /// `raw` must be a state value accepted by the linked runtime's matching
130    /// local-IRQ restore operation.
131    pub const unsafe fn from_raw(raw: usize) -> Self {
132        Self(raw)
133    }
134
135    /// Returns the runtime-owned representation of this saved state.
136    pub const fn into_raw(self) -> usize {
137        self.0
138    }
139}
140
141/// Logical CPU identifier exchanged with the operating-system runtime.
142#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
143#[repr(transparent)]
144pub struct RuntimeCpuId(u32);
145
146impl RuntimeCpuId {
147    /// Creates a logical CPU identifier.
148    pub const fn new(value: u32) -> Self {
149        Self(value)
150    }
151
152    /// Returns the numeric logical CPU identifier.
153    pub const fn as_u32(self) -> u32 {
154        self.0
155    }
156}
157
158/// Runtime-owned capability snapshot for one pinned scheduler CPU.
159///
160/// The paired fields are captured in one runtime operation, mirroring Linux's
161/// direct `this_rq()` lookup. The remote endpoint is the sole owner identity;
162/// its embedded CPU ID prevents a second architecture or registry lookup.
163#[derive(Clone, Copy, Debug, Eq, PartialEq)]
164#[repr(C)]
165pub struct CurrentCpuOwnerHandles {
166    local: CurrentCpuLocalHandle,
167    remote: CpuRemoteHandle,
168}
169
170impl CurrentCpuOwnerHandles {
171    /// Empty capability used when a scheduler-frame entry is rejected.
172    pub const NONE: Self = Self {
173        local: CurrentCpuLocalHandle::NONE,
174        remote: CpuRemoteHandle::NONE,
175    };
176
177    /// Creates one pinned current-CPU capability snapshot.
178    ///
179    /// # Safety
180    ///
181    /// `local` and `remote` must identify the paired owner-only and Arc-backed
182    /// scheduler endpoints for the pinned CPU. Every non-empty handle must
183    /// remain live until shutdown, and the caller must keep migration excluded
184    /// while the snapshot is used.
185    pub const unsafe fn new(local: CurrentCpuLocalHandle, remote: CpuRemoteHandle) -> Self {
186        Self { local, remote }
187    }
188
189    /// Returns the current CPU's owner-only scheduler handle.
190    pub const fn local(self) -> CurrentCpuLocalHandle {
191        self.local
192    }
193
194    /// Returns the current CPU's Arc-backed remote endpoint.
195    pub const fn remote(self) -> CpuRemoteHandle {
196        self.remote
197    }
198}
199
200pub use crate::sched::system::OwnerControlDrain;