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;