Skip to main content

strat9_kernel/process/sched_classes/
mod.rs

1//! Scheduling Policies and Classes
2//!
3//! Adapted from Asterinas.
4
5pub mod fair;
6pub mod idle;
7pub mod nice;
8pub mod real_time;
9
10use crate::process::task::Task;
11use alloc::sync::Arc;
12
13/// The scheduling policy of a task.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SchedPolicy {
16    /// Completely Fair Scheduler
17    Fair(nice::Nice),
18    /// Real-Time Round-Robin
19    RealTimeRR { prio: real_time::RealTimePriority },
20    /// Real-Time FIFO
21    RealTimeFifo { prio: real_time::RealTimePriority },
22    /// Idle task (lowest priority)
23    Idle,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum SchedPolicyKind {
28    Fair,
29    RealTime,
30    Idle,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum SchedClassId {
35    RealTime,
36    Fair,
37    Idle,
38}
39
40impl SchedClassId {
41    pub const ALL: [Self; 3] = [Self::RealTime, Self::Fair, Self::Idle];
42
43    /// Returns this as str.
44    pub const fn as_str(self) -> &'static str {
45        match self {
46            Self::RealTime => "rt",
47            Self::Fair => "fair",
48            Self::Idle => "idle",
49        }
50    }
51
52    /// Performs the parse operation.
53    pub fn parse(s: &str) -> Option<Self> {
54        if s.eq_ignore_ascii_case("rt")
55            || s.eq_ignore_ascii_case("real-time")
56            || s.eq_ignore_ascii_case("realtime")
57        {
58            Some(Self::RealTime)
59        } else if s.eq_ignore_ascii_case("fair") {
60            Some(Self::Fair)
61        } else if s.eq_ignore_ascii_case("idle") {
62            Some(Self::Idle)
63        } else {
64            None
65        }
66    }
67}
68
69#[derive(Debug, Clone, Copy)]
70pub struct SchedClassEntry {
71    pub id: SchedClassId,
72    pub name: &'static str,
73    pub rank: u8,
74}
75
76#[derive(Debug, Clone, Copy)]
77pub struct SchedClassTable {
78    entries: [SchedClassEntry; 3],
79    pick_order: [SchedClassId; 3],
80    steal_order: [SchedClassId; 2],
81    policy_map: [SchedClassId; 3], // index by SchedPolicyKind
82}
83
84impl Default for SchedClassTable {
85    /// Builds a default instance.
86    fn default() -> Self {
87        Self {
88            entries: [
89                SchedClassEntry {
90                    id: SchedClassId::RealTime,
91                    name: "real-time",
92                    rank: 0,
93                },
94                SchedClassEntry {
95                    id: SchedClassId::Fair,
96                    name: "fair",
97                    rank: 1,
98                },
99                SchedClassEntry {
100                    id: SchedClassId::Idle,
101                    name: "idle",
102                    rank: 2,
103                },
104            ],
105            pick_order: [
106                SchedClassId::RealTime,
107                SchedClassId::Fair,
108                SchedClassId::Idle,
109            ],
110            steal_order: [SchedClassId::Fair, SchedClassId::RealTime],
111            policy_map: [
112                SchedClassId::Fair,
113                SchedClassId::RealTime,
114                SchedClassId::Idle,
115            ],
116        }
117    }
118}
119
120impl SchedClassTable {
121    /// Creates a new instance.
122    pub fn new(pick_order: [SchedClassId; 3], steal_order: [SchedClassId; 2]) -> Self {
123        let mut out = Self::default();
124        let _ = out.set_pick_order(pick_order);
125        let _ = out.set_steal_order(steal_order);
126        out
127    }
128
129    /// Performs the kind index operation.
130    fn kind_index(kind: SchedPolicyKind) -> usize {
131        match kind {
132            SchedPolicyKind::Fair => 0,
133            SchedPolicyKind::RealTime => 1,
134            SchedPolicyKind::Idle => 2,
135        }
136    }
137
138    /// Performs the class index operation.
139    fn class_index(class: SchedClassId) -> usize {
140        match class {
141            SchedClassId::RealTime => 0,
142            SchedClassId::Fair => 1,
143            SchedClassId::Idle => 2,
144        }
145    }
146
147    /// Performs the refresh ranks operation.
148    fn refresh_ranks(&mut self) {
149        for entry in self.entries.iter_mut() {
150            entry.rank = match entry.id {
151                SchedClassId::RealTime => 255,
152                SchedClassId::Fair => 255,
153                SchedClassId::Idle => 255,
154            };
155        }
156        for (idx, class) in self.pick_order.iter().copied().enumerate() {
157            for entry in self.entries.iter_mut() {
158                if entry.id == class {
159                    entry.rank = idx as u8;
160                    break;
161                }
162            }
163        }
164    }
165
166    /// Performs the validate operation.
167    pub fn validate(&self) -> bool {
168        let mut seen = [false; 3];
169        for class in self.pick_order.iter().copied() {
170            seen[Self::class_index(class)] = true;
171        }
172        for class in SchedClassId::ALL.iter().copied() {
173            if !seen[Self::class_index(class)] {
174                return false;
175            }
176        }
177
178        if self.steal_order[0] == self.steal_order[1] {
179            return false;
180        }
181        if self.steal_order.iter().any(|c| *c == SchedClassId::Idle) {
182            return false;
183        }
184
185        if self.policy_map[Self::kind_index(SchedPolicyKind::Idle)] != SchedClassId::Idle {
186            return false;
187        }
188        if self.policy_map[Self::kind_index(SchedPolicyKind::Fair)] == SchedClassId::Idle {
189            return false;
190        }
191        if self.policy_map[Self::kind_index(SchedPolicyKind::RealTime)] == SchedClassId::Idle {
192            return false;
193        }
194        true
195    }
196
197    /// Performs the entries operation.
198    pub fn entries(&self) -> &[SchedClassEntry; 3] {
199        &self.entries
200    }
201
202    /// Performs the pick order operation.
203    pub fn pick_order(&self) -> &[SchedClassId; 3] {
204        &self.pick_order
205    }
206
207    /// Performs the steal order operation.
208    pub fn steal_order(&self) -> &[SchedClassId; 2] {
209        &self.steal_order
210    }
211
212    /// Performs the policy class operation.
213    pub fn policy_class(&self, kind: SchedPolicyKind) -> SchedClassId {
214        self.policy_map[Self::kind_index(kind)]
215    }
216
217    /// Performs the policy map operation.
218    pub fn policy_map(&self) -> &[SchedClassId; 3] {
219        &self.policy_map
220    }
221
222    /// Sets pick order.
223    pub fn set_pick_order(&mut self, pick_order: [SchedClassId; 3]) -> bool {
224        let prev = self.pick_order;
225        self.pick_order = pick_order;
226        if !self.validate() {
227            self.pick_order = prev;
228            return false;
229        }
230        self.refresh_ranks();
231        true
232    }
233
234    /// Sets steal order.
235    pub fn set_steal_order(&mut self, steal_order: [SchedClassId; 2]) -> bool {
236        let prev = self.steal_order;
237        self.steal_order = steal_order;
238        if !self.validate() {
239            self.steal_order = prev;
240            return false;
241        }
242        true
243    }
244
245    /// Sets policy class.
246    pub fn set_policy_class(&mut self, kind: SchedPolicyKind, class: SchedClassId) -> bool {
247        let idx = Self::kind_index(kind);
248        let prev = self.policy_map[idx];
249        self.policy_map[idx] = class;
250        if !self.validate() {
251            self.policy_map[idx] = prev;
252            return false;
253        }
254        true
255    }
256
257    /// Performs the class for policy operation.
258    pub fn class_for_policy(&self, policy: SchedPolicy) -> SchedClassId {
259        self.policy_class(policy.kind())
260    }
261
262    /// Performs the class for task operation.
263    pub fn class_for_task(&self, task: &Task) -> SchedClassId {
264        self.class_for_policy(task.sched_policy())
265    }
266}
267
268impl SchedPolicy {
269    /// Performs the kind operation.
270    pub fn kind(&self) -> SchedPolicyKind {
271        match self {
272            Self::Fair(_) => SchedPolicyKind::Fair,
273            Self::RealTimeRR { .. } | Self::RealTimeFifo { .. } => SchedPolicyKind::RealTime,
274            Self::Idle => SchedPolicyKind::Idle,
275        }
276    }
277}
278
279impl SchedPolicyKind {
280    /// Returns this as str.
281    pub fn as_str(self) -> &'static str {
282        match self {
283            Self::Fair => "fair",
284            Self::RealTime => "rt",
285            Self::Idle => "idle",
286        }
287    }
288
289    /// Performs the parse operation.
290    pub fn parse(s: &str) -> Option<Self> {
291        if s.eq_ignore_ascii_case("fair") {
292            Some(Self::Fair)
293        } else if s.eq_ignore_ascii_case("rt")
294            || s.eq_ignore_ascii_case("realtime")
295            || s.eq_ignore_ascii_case("real-time")
296        {
297            Some(Self::RealTime)
298        } else if s.eq_ignore_ascii_case("idle") {
299            Some(Self::Idle)
300        } else {
301            None
302        }
303    }
304}
305
306pub struct CurrentRuntime {
307    pub start_ticks: u64,
308    pub delta_ticks: u64,
309    pub period_delta_ticks: u64,
310}
311
312impl CurrentRuntime {
313    /// Creates a new instance.
314    pub fn new() -> Self {
315        Self {
316            start_ticks: crate::process::scheduler::ticks(),
317            delta_ticks: 0,
318            period_delta_ticks: 0,
319        }
320    }
321
322    /// Performs the update operation.
323    pub fn update(&mut self) {
324        let now = crate::process::scheduler::ticks();
325        self.delta_ticks = now.saturating_sub(core::mem::replace(&mut self.start_ticks, now));
326        self.period_delta_ticks += self.delta_ticks;
327    }
328}
329
330pub trait SchedClassRq {
331    /// Performs the enqueue operation.
332    fn enqueue(&mut self, task: Arc<Task>);
333    /// Performs the len operation.
334    fn len(&self) -> usize;
335    /// Returns whether empty.
336    fn is_empty(&self) -> bool {
337        self.len() == 0
338    }
339    /// Performs the pick next operation.
340    fn pick_next(&mut self) -> Option<Arc<Task>>;
341    /// Updates current.
342    fn update_current(&mut self, rt: &CurrentRuntime, task: &Task, is_yield: bool) -> bool;
343    /// Performs the remove operation.
344    fn remove(&mut self, task_id: crate::process::TaskId) -> bool;
345}