1use core::cell::Cell;
25use core::num::NonZeroU32;
26
27use crate::collections::list::{List, ListLink, ListNode};
28use crate::hil::time::{self, ConvertTicks, Ticks};
29use crate::platform::chip::Chip;
30use crate::process::ProcessSlot;
31use crate::process::StoppedExecutingReason;
32use crate::scheduler::{Scheduler, SchedulingDecision};
33
34#[derive(Default)]
35struct MfProcState {
36 us_used_this_queue: Cell<u32>,
38}
39
40pub struct MLFQProcessNode<'a> {
42 proc: &'static ProcessSlot,
43 state: MfProcState,
44 next: ListLink<'a, MLFQProcessNode<'a>>,
45}
46
47impl<'a> MLFQProcessNode<'a> {
48 pub fn new(proc: &'static ProcessSlot) -> MLFQProcessNode<'a> {
49 MLFQProcessNode {
50 proc,
51 state: MfProcState::default(),
52 next: ListLink::empty(),
53 }
54 }
55}
56
57impl<'a> ListNode<'a, MLFQProcessNode<'a>> for MLFQProcessNode<'a> {
58 fn next(&'a self) -> &'a ListLink<'a, MLFQProcessNode<'a>> {
59 &self.next
60 }
61}
62
63pub struct MLFQSched<'a, A: 'static + time::Alarm<'static>> {
64 alarm: &'static A,
65 pub processes: [List<'a, MLFQProcessNode<'a>>; 3], next_reset: Cell<A::Ticks>,
67 last_reset_check: Cell<A::Ticks>,
68 last_queue_idx: Cell<usize>,
69}
70
71impl<'a, A: 'static + time::Alarm<'static>> MLFQSched<'a, A> {
72 pub const PRIORITY_REFRESH_PERIOD_MS: u32 = 5000;
74 pub const NUM_QUEUES: usize = 3;
75
76 pub fn new(alarm: &'static A) -> Self {
77 Self {
78 alarm,
79 processes: [List::new(), List::new(), List::new()],
80 next_reset: Cell::new(A::Ticks::from(0)),
81 last_reset_check: Cell::new(A::Ticks::from(0)),
82 last_queue_idx: Cell::new(0),
83 }
84 }
85
86 fn get_timeslice_us(&self, queue_idx: usize) -> u32 {
87 match queue_idx {
88 0 => 10000,
89 1 => 20000,
90 2 => 50000,
91 _ => panic!("invalid queue idx"),
92 }
93 }
94
95 fn redeem_all_procs(&self) {
96 for queue in self.processes.iter().skip(1) {
97 if let Some(proc) = queue.pop_head() {
98 self.processes[0].push_tail(proc)
99 }
100 }
101 }
102
103 fn get_next_ready_process_node(&self) -> (Option<&MLFQProcessNode<'a>>, usize) {
107 for (idx, queue) in self.processes.iter().enumerate() {
108 let next = queue
109 .iter()
110 .find(|node_ref| node_ref.proc.get().is_some_and(|proc| proc.ready()));
111 if let Some(inner) = next {
112 loop {
114 let cur = queue.pop_head();
115 if let Some(node) = cur {
116 if core::ptr::eq(node, inner) {
117 queue.push_head(node);
118 return (next, idx);
120 } else {
121 queue.push_tail(node);
122 }
123 }
124 }
125 }
126 }
127 (None, 0)
128 }
129}
130
131impl<A: 'static + time::Alarm<'static>, C: Chip> Scheduler<C> for MLFQSched<'_, A> {
132 fn next(&self) -> SchedulingDecision {
133 let now = self.alarm.now();
134 let next_reset = self.next_reset.get();
135 let last_reset_check = self.last_reset_check.get();
136
137 if !now.within_range(last_reset_check, next_reset) {
140 self.next_reset
142 .set(now.wrapping_add(self.alarm.ticks_from_ms(Self::PRIORITY_REFRESH_PERIOD_MS)));
143 self.redeem_all_procs();
144 }
145 self.last_reset_check.set(now);
146 let (node_ref_opt, queue_idx) = self.get_next_ready_process_node();
147 if node_ref_opt.is_none() {
148 return SchedulingDecision::TrySleep;
149 }
150 let node_ref = node_ref_opt.unwrap();
151 let timeslice = self.get_timeslice_us(queue_idx) - node_ref.state.us_used_this_queue.get();
153 let next = node_ref.proc.get().unwrap().processid();
154 self.last_queue_idx.set(queue_idx);
155
156 SchedulingDecision::RunProcess((next, NonZeroU32::new(timeslice)))
157 }
158
159 fn result(&self, result: StoppedExecutingReason, execution_time_us: Option<u32>) {
160 let execution_time_us = execution_time_us.unwrap(); let queue_idx = self.last_queue_idx.get();
162 let node_ref = self.processes[queue_idx].head().unwrap();
164
165 let punish = result == StoppedExecutingReason::TimesliceExpired;
166 if punish {
167 node_ref.state.us_used_this_queue.set(0); let next_queue = if queue_idx == Self::NUM_QUEUES - 1 {
169 queue_idx
170 } else {
171 queue_idx + 1
172 };
173 self.processes[next_queue].push_tail(self.processes[queue_idx].pop_head().unwrap());
174 } else {
175 node_ref
177 .state
178 .us_used_this_queue
179 .update(|runtime| runtime + execution_time_us);
180 self.processes[queue_idx].push_tail(self.processes[queue_idx].pop_head().unwrap());
181 }
182 }
183}