use alloc::sync::Arc;
use core::cmp::Ordering;
use crate::{
runtime::{config::DEFAULT_RR_QUANTUM_NS, lock::IrqTicketLock},
sched::{
SchedulerTimestamp,
algorithm::{SCHEDULER_TIME_HALF_RANGE, scheduler_time_cmp},
},
thread::TaskError,
};
pub(crate) const DEADLINE_CLASS_RANK: u8 = 1;
pub(crate) const REALTIME_CLASS_RANK: u8 = 2;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Nice(i8);
impl Nice {
pub const ZERO: Self = Self(0);
pub const LOWEST: Self = Self(19);
pub const fn new(value: i8) -> Result<Self, TaskError> {
if value >= -20 && value <= 19 {
Ok(Self(value))
} else {
Err(TaskError::InvalidNice(value))
}
}
pub const fn get(self) -> i8 {
self.0
}
pub const fn weight(self) -> u32 {
NICE_WEIGHTS[(self.0 + 20) as usize]
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct RtPriority(u8);
impl RtPriority {
pub const fn new(value: u8) -> Result<Self, TaskError> {
if value >= 1 && value <= 99 {
Ok(Self(value))
} else {
Err(TaskError::InvalidRtPriority(value))
}
}
pub const fn get(self) -> u8 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FairMode {
Normal,
Batch,
Idle,
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DeadlineFlags(u32);
impl DeadlineFlags {
pub const NONE: Self = Self(0);
pub const RECLAIM: Self = Self(1 << 0);
pub const DL_OVERRUN: Self = Self(1 << 1);
pub const RESET_ON_FORK: Self = Self(1 << 2);
const KNOWN_BITS: u32 = Self::RECLAIM.0 | Self::DL_OVERRUN.0 | Self::RESET_ON_FORK.0;
pub const fn from_bits(bits: u32) -> Result<Self, TaskError> {
if bits & !Self::KNOWN_BITS == 0 {
Ok(Self(bits))
} else {
Err(TaskError::UnsupportedDeadlineFlags(bits))
}
}
pub const fn bits(self) -> u32 {
self.0
}
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
}
impl core::ops::BitOr for DeadlineFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DeadlinePolicy {
runtime_ns: u64,
deadline_ns: u64,
period_ns: u64,
flags: DeadlineFlags,
}
impl DeadlinePolicy {
pub const fn new(
runtime_ns: u64,
deadline_ns: u64,
period_ns: u64,
flags: DeadlineFlags,
) -> Result<Self, TaskError> {
if runtime_ns > 0
&& runtime_ns <= deadline_ns
&& deadline_ns <= period_ns
&& period_ns < SCHEDULER_TIME_HALF_RANGE
{
Ok(Self {
runtime_ns,
deadline_ns,
period_ns,
flags,
})
} else {
Err(TaskError::InvalidDeadline {
runtime_ns,
deadline_ns,
period_ns,
})
}
}
pub const fn runtime_ns(self) -> u64 {
self.runtime_ns
}
pub const fn deadline_ns(self) -> u64 {
self.deadline_ns
}
pub const fn period_ns(self) -> u64 {
self.period_ns
}
pub const fn flags(self) -> DeadlineFlags {
self.flags
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SchedulePolicy {
KernelStop,
Fair {
nice: Nice,
mode: FairMode,
},
Fifo {
priority: RtPriority,
},
RoundRobin {
priority: RtPriority,
quantum_ns: u64,
},
Deadline(DeadlinePolicy),
}
impl SchedulePolicy {
pub(crate) const IDLE_POLICY_WEIGHT: u32 = 3;
pub(crate) const fn placement_demand(self) -> u64 {
match self {
Self::KernelStop => 0,
Self::Fair {
mode: FairMode::Idle,
..
} => Self::IDLE_POLICY_WEIGHT as u64,
Self::Fair { nice, .. } => nice.weight() as u64,
Self::Fifo { .. } | Self::RoundRobin { .. } | Self::Deadline(_) => {
Nice::ZERO.weight() as u64
}
}
}
pub(crate) const fn fair_demand(self) -> u64 {
match self {
Self::Fair { .. } => self.placement_demand(),
Self::KernelStop | Self::Fifo { .. } | Self::RoundRobin { .. } | Self::Deadline(_) => 0,
}
}
pub const fn validate(self) -> Result<(), TaskError> {
match self {
Self::RoundRobin { quantum_ns: 0, .. } => Err(TaskError::InvalidRoundRobinQuantum),
_ => Ok(()),
}
}
pub const fn fair(nice: Nice, mode: FairMode) -> Self {
Self::Fair { nice, mode }
}
#[doc(hidden)]
pub const fn kernel_stop() -> Self {
Self::KernelStop
}
pub const fn fifo(priority: RtPriority) -> Self {
Self::Fifo { priority }
}
pub const fn round_robin(priority: RtPriority) -> Self {
Self::RoundRobin {
priority,
quantum_ns: DEFAULT_RR_QUANTUM_NS,
}
}
pub const fn round_robin_with_quantum(
priority: RtPriority,
quantum_ns: u64,
) -> Result<Self, TaskError> {
if quantum_ns == 0 {
Err(TaskError::InvalidRoundRobinQuantum)
} else {
Ok(Self::RoundRobin {
priority,
quantum_ns,
})
}
}
pub const fn deadline(policy: DeadlinePolicy) -> Self {
Self::Deadline(policy)
}
pub const fn class_rank(&self) -> u8 {
match self {
Self::KernelStop => 0,
Self::Deadline(_) => DEADLINE_CLASS_RANK,
Self::Fifo { .. } | Self::RoundRobin { .. } => REALTIME_CLASS_RANK,
Self::Fair { .. } => 3,
}
}
pub(crate) const fn rt_priority(self) -> Option<RtPriority> {
match self {
Self::Fifo { priority } | Self::RoundRobin { priority, .. } => Some(priority),
Self::KernelStop | Self::Fair { .. } | Self::Deadline(_) => None,
}
}
pub(crate) const fn scheduling_key(self, sequence: u64) -> SchedulingKey {
let urgency = self.scheduling_urgency();
SchedulingKey::new(urgency.class_rank(), urgency.primary(), sequence)
}
pub(crate) const fn scheduling_urgency(&self) -> SchedulingUrgency {
let primary = match self {
Self::KernelStop => 0,
Self::Deadline(policy) => policy.deadline_ns(),
Self::Fifo { priority } | Self::RoundRobin { priority, .. } => {
99 - priority.get() as u64
}
Self::Fair { nice, .. } => (nice.get() as i16 + 20) as u64,
};
SchedulingUrgency::new(self.class_rank(), primary)
}
}
impl Default for SchedulePolicy {
fn default() -> Self {
Self::fair(Nice::ZERO, FairMode::Normal)
}
}
fn density_exceeds_reservation(
remaining_runtime_ns: u128,
time_to_deadline_ns: u64,
policy: DeadlinePolicy,
) -> bool {
remaining_runtime_ns * policy.deadline_ns() as u128
> policy.runtime_ns() as u128 * time_to_deadline_ns as u128
}
fn revised_wakeup_runtime(time_to_deadline_ns: u64, policy: DeadlinePolicy) -> i128 {
let runtime_ns =
(policy.runtime_ns() as u128 * time_to_deadline_ns as u128) / policy.deadline_ns() as u128;
runtime_ns as i128
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct SchedulingUrgency {
class_rank: u8,
primary: u64,
}
impl SchedulingUrgency {
pub const fn new(class_rank: u8, primary: u64) -> Self {
Self {
class_rank,
primary,
}
}
pub const fn class_rank(self) -> u8 {
self.class_rank
}
pub const fn primary(self) -> u64 {
self.primary
}
}
impl Ord for SchedulingUrgency {
fn cmp(&self, other: &Self) -> Ordering {
self.class_rank.cmp(&other.class_rank).then_with(|| {
if self.class_rank == DEADLINE_CLASS_RANK {
scheduler_time_cmp(self.primary, other.primary)
} else {
self.primary.cmp(&other.primary)
}
})
}
}
impl PartialOrd for SchedulingUrgency {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct SchedulingKey {
class_rank: u8,
primary: u64,
sequence: u64,
}
impl SchedulingKey {
pub const fn new(class_rank: u8, primary: u64, sequence: u64) -> Self {
Self {
class_rank,
primary,
sequence,
}
}
pub const fn class_rank(self) -> u8 {
self.class_rank
}
pub const fn primary(self) -> u64 {
self.primary
}
}
impl Ord for SchedulingKey {
fn cmp(&self, other: &Self) -> Ordering {
self.class_rank
.cmp(&other.class_rank)
.then_with(|| {
if self.class_rank == DEADLINE_CLASS_RANK {
scheduler_time_cmp(self.primary, other.primary)
} else {
self.primary.cmp(&other.primary)
}
})
.then_with(|| self.sequence.cmp(&other.sequence))
}
}
impl PartialOrd for SchedulingKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
const NICE_WEIGHTS: [u32; 40] = [
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87,
70, 56, 45, 36, 29, 23, 18, 15,
];
#[cfg(test)]
mod tests;
mod deadline;
pub(crate) use deadline::{DeadlineEntity, DeadlineServer};