1pub 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
19pub unsafe fn current_needs_reschedule_pinned() -> Result<bool, TaskError> {
27 Ok(current_cpu_remote()
28 .ok_or(TaskError::NotInitialized)?
29 .needs_reschedule())
30}
31
32pub 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
44pub fn current_cpu_needs_resched() -> Result<bool, TaskError> {
46 let _pin = PreemptScope::enter();
47 unsafe { current_needs_reschedule_pinned() }
50}
51
52#[cfg(feature = "fault-injection")]
55pub fn current_immediate_preemption_requested() -> Result<bool, TaskError> {
56 let _pin = PreemptScope::enter();
57 Ok(current_cpu_remote()
58 .ok_or(TaskError::NotInitialized)?
59 .immediate_preemption_requested())
60}
61
62#[doc(hidden)]
72pub unsafe fn finish_current_cpu_idle_polling() -> Result<(), TaskError> {
73 let remote = current_cpu_remote().ok_or(TaskError::NotInitialized)?;
74 remote.finish_idle_wait();
75 Ok(())
76}
77
78pub fn idle_current_cpu_once() -> Result<(), TaskError> {
80 validate_schedule_context(RuntimeScheduleOrigin::Preempt)?;
81 let may_wait = {
82 let cpu = runtime_current_cpu()?;
83 cpu.prepare_idle_wait()
84 };
85 if may_wait {
86 task_runtime::wait_for_interrupt();
87 }
88 Ok(())
89}
90use crate::runtime::handle::opaque_handle;
91
92opaque_handle!(
93 CurrentCpuLocalHandle,
98 "runtime::cpu"
99);
100opaque_handle!(
101 CpuRemoteHandle,
112 "runtime::cpu"
113);
114opaque_handle!(
115 IrqGuardToken,
117 "runtime::cpu"
118);
119opaque_handle!(
120 PreemptGuardToken,
122 "runtime::cpu"
123);
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
131#[repr(transparent)]
132pub struct LocalIrqState(usize);
133
134impl LocalIrqState {
135 pub const unsafe fn from_raw(raw: usize) -> Self {
142 Self(raw)
143 }
144
145 pub const fn into_raw(self) -> usize {
147 self.0
148 }
149}
150
151#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
153#[repr(transparent)]
154pub struct RuntimeCpuId(u32);
155
156impl RuntimeCpuId {
157 pub const fn new(value: u32) -> Self {
159 Self(value)
160 }
161
162 pub const fn as_u32(self) -> u32 {
164 self.0
165 }
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
174#[repr(C)]
175pub struct CurrentCpuOwnerHandles {
176 local: CurrentCpuLocalHandle,
177 remote: CpuRemoteHandle,
178}
179
180impl CurrentCpuOwnerHandles {
181 pub const NONE: Self = Self {
183 local: CurrentCpuLocalHandle::NONE,
184 remote: CpuRemoteHandle::NONE,
185 };
186
187 pub const unsafe fn new(local: CurrentCpuLocalHandle, remote: CpuRemoteHandle) -> Self {
196 Self { local, remote }
197 }
198
199 pub const fn local(self) -> CurrentCpuLocalHandle {
201 self.local
202 }
203
204 pub const fn remote(self) -> CpuRemoteHandle {
206 self.remote
207 }
208}
209
210pub use crate::sched::system::OwnerControlDrain;
211
212#[cfg(feature = "fault-injection")]
214#[derive(Clone, Copy, Debug)]
215#[repr(u8)]
216pub enum IdleOfflineRejection {
217 Unclassified = 0,
219 PlacementPublication = 1,
221 ThreadTarget = 2,
223 OwnerPublication = 3,
225 CpuState = 4,
227 ThreadOwnership = 5,
229 SchedulerWork = 6,
231}
232
233#[cfg(feature = "fault-injection")]
234static IDLE_OFFLINE_REJECTION: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
235
236#[cfg(feature = "fault-injection")]
237pub(crate) fn record_idle_offline_rejection(reason: IdleOfflineRejection) {
238 IDLE_OFFLINE_REJECTION.store(reason as u8, core::sync::atomic::Ordering::Release);
239}
240
241#[cfg(feature = "fault-injection")]
243pub fn idle_offline_rejection() -> IdleOfflineRejection {
244 match IDLE_OFFLINE_REJECTION.load(core::sync::atomic::Ordering::Acquire) {
245 1 => IdleOfflineRejection::PlacementPublication,
246 2 => IdleOfflineRejection::ThreadTarget,
247 3 => IdleOfflineRejection::OwnerPublication,
248 4 => IdleOfflineRejection::CpuState,
249 5 => IdleOfflineRejection::ThreadOwnership,
250 6 => IdleOfflineRejection::SchedulerWork,
251 _ => IdleOfflineRejection::Unclassified,
252 }
253}
254
255#[cfg(feature = "fault-injection")]
263pub fn probe_idle_cpu_round_trip(publish_work_after_drain: bool) -> Result<(), TaskError> {
264 use crate::runtime::context::{RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system};
265 validate_schedule_context(RuntimeScheduleOrigin::Preempt)?;
266 let system = runtime_task_system()?;
267 let mut irq = RuntimeIrqGuard::enter();
268 let mut cpu = runtime_current_cpu_mut(&mut irq)?;
269 if cpu.remote().current_thread() != cpu.remote().idle_thread() {
270 return Err(TaskError::NotReady);
271 }
272 if publish_work_after_drain {
273 cpu.request_scheduler_work();
276 }
277 record_idle_offline_rejection(IdleOfflineRejection::Unclassified);
278 system.take_cpu_offline(cpu.as_mut())?;
279 assert_eq!(cpu.remote().lifecycle_state(), CpuLifecycleState::Offline);
280 assert!(system.cpu_remote(cpu.owner()).is_none());
281 system
283 .bring_cpu_online(cpu.as_mut())
284 .expect("idle CPU re-online failed");
285 assert_eq!(cpu.remote().lifecycle_state(), CpuLifecycleState::Online);
286 Ok(())
287}
288
289#[cfg(feature = "fault-injection")]
291pub fn notify_idle_cpu_probe(cpu: RuntimeCpuId) -> Result<(), TaskError> {
292 let system = crate::runtime::context::runtime_task_system()?;
293 let remote = system
294 .cpu_remote(crate::sched::CpuId::new(cpu.as_u32()))
295 .ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
296 if remote.kick_scheduler_work() {
297 Ok(())
298 } else {
299 Err(TaskError::CpuOffline(cpu.as_u32()))
300 }
301}
302
303#[cfg(feature = "fault-injection")]
305#[derive(Clone, Copy, Debug)]
306pub enum IdleOfflineReader {
307 OwnerDelivery,
309 IdleBalance,
311}
312
313#[cfg(feature = "fault-injection")]
317pub fn with_idle_offline_reader<T>(
318 cpu: RuntimeCpuId,
319 reader: IdleOfflineReader,
320 action: impl FnOnce(&CpuRemote) -> T,
321) -> Result<T, TaskError> {
322 crate::thread::current::validate_blocking_context()?;
323 let system = crate::runtime::context::runtime_task_system()?;
324 let remote = system
325 .cpu_remote(crate::sched::CpuId::new(cpu.as_u32()))
326 .ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
327 record_idle_offline_rejection(IdleOfflineRejection::Unclassified);
328 match reader {
329 IdleOfflineReader::OwnerDelivery => {
330 let _publication = remote
331 .begin_owner_delivery()
332 .ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
333 Ok(action(remote))
334 }
335 IdleOfflineReader::IdleBalance => {
336 let crate::sched::system::IdlePullReservation::Started(reservation) =
337 remote.begin_idle_pull()
338 else {
339 return Err(TaskError::NotReady);
340 };
341 let Some(mut claim) = remote.claim_idle_pull(reservation) else {
342 remote.cancel_idle_pull(reservation);
343 return Err(TaskError::NotReady);
344 };
345 if !claim.commit() {
346 return Err(TaskError::NotReady);
347 }
348 Ok(action(remote))
349 }
350 }
351}