pub use crate::{
runtime::clock::{RqClockSample, SchedulerDeadlineUpdate, SchedulerRuntimeDeadline},
sched::system::{
CpuLifecycleState, CpuLoadSummary, CpuLocal, CpuLocalOwnerBorrow, CpuRemote, CpuSnapshot,
},
};
use crate::{
runtime::{
context::{current_cpu_remote, runtime_current_cpu, validate_schedule_context},
lock::PreemptScope,
switch::RuntimeScheduleOrigin,
task_runtime,
},
thread::TaskError,
};
pub unsafe fn current_needs_reschedule_pinned() -> Result<bool, TaskError> {
Ok(current_cpu_remote()
.ok_or(TaskError::NotInitialized)?
.needs_reschedule())
}
pub unsafe fn current_needs_immediate_scheduler_work_pinned() -> Result<bool, TaskError> {
Ok(current_cpu_remote()
.ok_or(TaskError::NotInitialized)?
.needs_immediate_scheduler_work())
}
pub fn current_cpu_needs_resched() -> Result<bool, TaskError> {
let _pin = PreemptScope::enter();
unsafe { current_needs_reschedule_pinned() }
}
#[cfg(feature = "fault-injection")]
pub fn current_immediate_preemption_requested() -> Result<bool, TaskError> {
let _pin = PreemptScope::enter();
Ok(current_cpu_remote()
.ok_or(TaskError::NotInitialized)?
.immediate_preemption_requested())
}
#[doc(hidden)]
pub unsafe fn finish_current_cpu_idle_polling() -> Result<(), TaskError> {
let remote = current_cpu_remote().ok_or(TaskError::NotInitialized)?;
remote.finish_idle_wait();
Ok(())
}
pub fn idle_current_cpu_once() -> Result<(), TaskError> {
validate_schedule_context(RuntimeScheduleOrigin::Preempt)?;
let may_wait = {
let cpu = runtime_current_cpu()?;
cpu.prepare_idle_wait()
};
if may_wait {
task_runtime::wait_for_interrupt();
}
Ok(())
}
use crate::runtime::handle::opaque_handle;
opaque_handle!(
CurrentCpuLocalHandle,
"runtime::cpu"
);
opaque_handle!(
CpuRemoteHandle,
"runtime::cpu"
);
opaque_handle!(
IrqGuardToken,
"runtime::cpu"
);
opaque_handle!(
PreemptGuardToken,
"runtime::cpu"
);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct LocalIrqState(usize);
impl LocalIrqState {
pub const unsafe fn from_raw(raw: usize) -> Self {
Self(raw)
}
pub const fn into_raw(self) -> usize {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct RuntimeCpuId(u32);
impl RuntimeCpuId {
pub const fn new(value: u32) -> Self {
Self(value)
}
pub const fn as_u32(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct CurrentCpuOwnerHandles {
local: CurrentCpuLocalHandle,
remote: CpuRemoteHandle,
}
impl CurrentCpuOwnerHandles {
pub const NONE: Self = Self {
local: CurrentCpuLocalHandle::NONE,
remote: CpuRemoteHandle::NONE,
};
pub const unsafe fn new(local: CurrentCpuLocalHandle, remote: CpuRemoteHandle) -> Self {
Self { local, remote }
}
pub const fn local(self) -> CurrentCpuLocalHandle {
self.local
}
pub const fn remote(self) -> CpuRemoteHandle {
self.remote
}
}
pub use crate::sched::system::OwnerControlDrain;
#[cfg(feature = "fault-injection")]
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum IdleOfflineRejection {
Unclassified = 0,
PlacementPublication = 1,
ThreadTarget = 2,
OwnerPublication = 3,
CpuState = 4,
ThreadOwnership = 5,
SchedulerWork = 6,
}
#[cfg(feature = "fault-injection")]
static IDLE_OFFLINE_REJECTION: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
#[cfg(feature = "fault-injection")]
pub(crate) fn record_idle_offline_rejection(reason: IdleOfflineRejection) {
IDLE_OFFLINE_REJECTION.store(reason as u8, core::sync::atomic::Ordering::Release);
}
#[cfg(feature = "fault-injection")]
pub fn idle_offline_rejection() -> IdleOfflineRejection {
match IDLE_OFFLINE_REJECTION.load(core::sync::atomic::Ordering::Acquire) {
1 => IdleOfflineRejection::PlacementPublication,
2 => IdleOfflineRejection::ThreadTarget,
3 => IdleOfflineRejection::OwnerPublication,
4 => IdleOfflineRejection::CpuState,
5 => IdleOfflineRejection::ThreadOwnership,
6 => IdleOfflineRejection::SchedulerWork,
_ => IdleOfflineRejection::Unclassified,
}
}
#[cfg(feature = "fault-injection")]
pub fn probe_idle_cpu_round_trip(publish_work_after_drain: bool) -> Result<(), TaskError> {
use crate::runtime::context::{RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system};
validate_schedule_context(RuntimeScheduleOrigin::Preempt)?;
let system = runtime_task_system()?;
let mut irq = RuntimeIrqGuard::enter();
let mut cpu = runtime_current_cpu_mut(&mut irq)?;
if cpu.remote().current_thread() != cpu.remote().idle_thread() {
return Err(TaskError::NotReady);
}
if publish_work_after_drain {
cpu.request_scheduler_work();
}
record_idle_offline_rejection(IdleOfflineRejection::Unclassified);
system.take_cpu_offline(cpu.as_mut())?;
assert_eq!(cpu.remote().lifecycle_state(), CpuLifecycleState::Offline);
assert!(system.cpu_remote(cpu.owner()).is_none());
system
.bring_cpu_online(cpu.as_mut())
.expect("idle CPU re-online failed");
assert_eq!(cpu.remote().lifecycle_state(), CpuLifecycleState::Online);
Ok(())
}
#[cfg(feature = "fault-injection")]
pub fn notify_idle_cpu_probe(cpu: RuntimeCpuId) -> Result<(), TaskError> {
let system = crate::runtime::context::runtime_task_system()?;
let remote = system
.cpu_remote(crate::sched::CpuId::new(cpu.as_u32()))
.ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
if remote.kick_scheduler_work() {
Ok(())
} else {
Err(TaskError::CpuOffline(cpu.as_u32()))
}
}
#[cfg(feature = "fault-injection")]
#[derive(Clone, Copy, Debug)]
pub enum IdleOfflineReader {
OwnerDelivery,
IdleBalance,
}
#[cfg(feature = "fault-injection")]
pub fn with_idle_offline_reader<T>(
cpu: RuntimeCpuId,
reader: IdleOfflineReader,
action: impl FnOnce(&CpuRemote) -> T,
) -> Result<T, TaskError> {
crate::thread::current::validate_blocking_context()?;
let system = crate::runtime::context::runtime_task_system()?;
let remote = system
.cpu_remote(crate::sched::CpuId::new(cpu.as_u32()))
.ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
record_idle_offline_rejection(IdleOfflineRejection::Unclassified);
match reader {
IdleOfflineReader::OwnerDelivery => {
let _publication = remote
.begin_owner_delivery()
.ok_or(TaskError::CpuOffline(cpu.as_u32()))?;
Ok(action(remote))
}
IdleOfflineReader::IdleBalance => {
let crate::sched::system::IdlePullReservation::Started(reservation) =
remote.begin_idle_pull()
else {
return Err(TaskError::NotReady);
};
let Some(mut claim) = remote.claim_idle_pull(reservation) else {
remote.cancel_idle_pull(reservation);
return Err(TaskError::NotReady);
};
if !claim.commit() {
return Err(TaskError::NotReady);
}
Ok(action(remote))
}
}
}