|
| 1 | +use alloc::{collections::BTreeMap, sync::Arc}; |
| 2 | +use core::ops::Deref; |
| 3 | +use core::sync::atomic::{AtomicIsize, Ordering}; |
| 4 | + |
| 5 | +use crate::BaseScheduler; |
| 6 | + |
| 7 | +/// task for CFS |
| 8 | +pub struct CFSTask<T> { |
| 9 | + inner: T, |
| 10 | + init_vruntime: AtomicIsize, |
| 11 | + delta: AtomicIsize, |
| 12 | + nice: AtomicIsize, |
| 13 | + id: AtomicIsize, |
| 14 | +} |
| 15 | + |
| 16 | +// https://elixir.bootlin.com/linux/latest/source/include/linux/sched/prio.h |
| 17 | + |
| 18 | +const NICE_RANGE_POS: usize = 19; // MAX_NICE in Linux |
| 19 | +const NICE_RANGE_NEG: usize = 20; // -MIN_NICE in Linux, the range of nice is [MIN_NICE, MAX_NICE] |
| 20 | + |
| 21 | +// https://elixir.bootlin.com/linux/latest/source/kernel/sched/core.c |
| 22 | + |
| 23 | +const NICE2WEIGHT_POS: [isize; NICE_RANGE_POS + 1] = [ |
| 24 | + 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87, 70, 56, 45, 36, 29, 23, 18, 15, |
| 25 | +]; |
| 26 | +const NICE2WEIGHT_NEG: [isize; NICE_RANGE_NEG + 1] = [ |
| 27 | + 1024, 1277, 1586, 1991, 2501, 3121, 3906, 4904, 6100, 7620, 9548, 11916, 14949, 18705, 23254, |
| 28 | + 29154, 36291, 46273, 56483, 71755, 88761, |
| 29 | +]; |
| 30 | + |
| 31 | +impl<T> CFSTask<T> { |
| 32 | + /// new with default values |
| 33 | + pub const fn new(inner: T) -> Self { |
| 34 | + Self { |
| 35 | + inner, |
| 36 | + init_vruntime: AtomicIsize::new(0_isize), |
| 37 | + delta: AtomicIsize::new(0_isize), |
| 38 | + nice: AtomicIsize::new(0_isize), |
| 39 | + id: AtomicIsize::new(0_isize), |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + fn get_weight(&self) -> isize { |
| 44 | + let nice = self.nice.load(Ordering::Acquire); |
| 45 | + if nice >= 0 { |
| 46 | + NICE2WEIGHT_POS[nice as usize] |
| 47 | + } else { |
| 48 | + NICE2WEIGHT_NEG[(-nice) as usize] |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + fn get_id(&self) -> isize { |
| 53 | + self.id.load(Ordering::Acquire) |
| 54 | + } |
| 55 | + |
| 56 | + fn get_vruntime(&self) -> isize { |
| 57 | + if self.nice.load(Ordering::Acquire) == 0 { |
| 58 | + self.init_vruntime.load(Ordering::Acquire) + self.delta.load(Ordering::Acquire) |
| 59 | + } else { |
| 60 | + self.init_vruntime.load(Ordering::Acquire) |
| 61 | + + self.delta.load(Ordering::Acquire) * 1024 / self.get_weight() |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + fn set_vruntime(&self, v: isize) { |
| 66 | + self.init_vruntime.store(v, Ordering::Release); |
| 67 | + } |
| 68 | + |
| 69 | + // Simple Implementation: no change in vruntime. |
| 70 | + // Only modifying priority of current process is supported currently. |
| 71 | + fn set_priority(&self, nice: isize) { |
| 72 | + let current_init_vruntime = self.get_vruntime(); |
| 73 | + self.init_vruntime |
| 74 | + .store(current_init_vruntime, Ordering::Release); |
| 75 | + self.delta.store(0, Ordering::Release); |
| 76 | + self.nice.store(nice, Ordering::Release); |
| 77 | + } |
| 78 | + |
| 79 | + fn set_id(&self, id: isize) { |
| 80 | + self.id.store(id, Ordering::Release); |
| 81 | + } |
| 82 | + |
| 83 | + fn task_tick(&self) { |
| 84 | + self.delta.fetch_add(1, Ordering::Release); |
| 85 | + } |
| 86 | + |
| 87 | + /// Returns a reference to the inner task struct. |
| 88 | + pub const fn inner(&self) -> &T { |
| 89 | + &self.inner |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +impl<T> Deref for CFSTask<T> { |
| 94 | + type Target = T; |
| 95 | + fn deref(&self) -> &Self::Target { |
| 96 | + &self.inner |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +/// A simple [Completely Fair Scheduler][1] (CFS). |
| 101 | +/// |
| 102 | +/// [1]: https://en.wikipedia.org/wiki/Completely_Fair_Scheduler |
| 103 | +pub struct CFScheduler<T> { |
| 104 | + ready_queue: BTreeMap<(isize, isize), Arc<CFSTask<T>>>, // (vruntime, taskid) |
| 105 | + min_vruntime: Option<AtomicIsize>, |
| 106 | + id_pool: AtomicIsize, |
| 107 | +} |
| 108 | + |
| 109 | +impl<T> CFScheduler<T> { |
| 110 | + /// Creates a new empty [`CFScheduler`]. |
| 111 | + pub const fn new() -> Self { |
| 112 | + Self { |
| 113 | + ready_queue: BTreeMap::new(), |
| 114 | + min_vruntime: None, |
| 115 | + id_pool: AtomicIsize::new(0_isize), |
| 116 | + } |
| 117 | + } |
| 118 | + /// get the name of scheduler |
| 119 | + pub fn scheduler_name() -> &'static str { |
| 120 | + "Completely Fair" |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +impl<T> BaseScheduler for CFScheduler<T> { |
| 125 | + type SchedItem = Arc<CFSTask<T>>; |
| 126 | + |
| 127 | + fn init(&mut self) {} |
| 128 | + |
| 129 | + fn add_task(&mut self, task: Self::SchedItem) { |
| 130 | + if self.min_vruntime.is_none() { |
| 131 | + self.min_vruntime = Some(AtomicIsize::new(0_isize)); |
| 132 | + } |
| 133 | + let vruntime = self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire); |
| 134 | + let taskid = self.id_pool.fetch_add(1, Ordering::Release); |
| 135 | + task.set_vruntime(vruntime); |
| 136 | + task.set_id(taskid); |
| 137 | + self.ready_queue.insert((vruntime, taskid), task); |
| 138 | + if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() { |
| 139 | + self.min_vruntime = Some(AtomicIsize::new(*min_vruntime)); |
| 140 | + } else { |
| 141 | + self.min_vruntime = None; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + fn remove_task(&mut self, task: &Self::SchedItem) -> Option<Self::SchedItem> { |
| 146 | + if let Some((_, tmp)) = self |
| 147 | + .ready_queue |
| 148 | + .remove_entry(&(task.clone().get_vruntime(), task.clone().get_id())) |
| 149 | + { |
| 150 | + if let Some(((min_vruntime, _), _)) = self.ready_queue.first_key_value() { |
| 151 | + self.min_vruntime = Some(AtomicIsize::new(*min_vruntime)); |
| 152 | + } else { |
| 153 | + self.min_vruntime = None; |
| 154 | + } |
| 155 | + Some(tmp) |
| 156 | + } else { |
| 157 | + None |
| 158 | + } |
| 159 | + } |
| 160 | + |
| 161 | + fn pick_next_task(&mut self) -> Option<Self::SchedItem> { |
| 162 | + if let Some((_, v)) = self.ready_queue.pop_first() { |
| 163 | + Some(v) |
| 164 | + } else { |
| 165 | + None |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) { |
| 170 | + let taskid = self.id_pool.fetch_add(1, Ordering::Release); |
| 171 | + prev.set_id(taskid); |
| 172 | + self.ready_queue |
| 173 | + .insert((prev.clone().get_vruntime(), taskid), prev); |
| 174 | + } |
| 175 | + |
| 176 | + fn task_tick(&mut self, current: &Self::SchedItem) -> bool { |
| 177 | + current.task_tick(); |
| 178 | + self.min_vruntime.is_none() |
| 179 | + || current.get_vruntime() > self.min_vruntime.as_mut().unwrap().load(Ordering::Acquire) |
| 180 | + } |
| 181 | + |
| 182 | + fn set_priority(&mut self, task: &Self::SchedItem, prio: isize) -> bool { |
| 183 | + if (-20..=19).contains(&prio) { |
| 184 | + task.set_priority(prio); |
| 185 | + true |
| 186 | + } else { |
| 187 | + false |
| 188 | + } |
| 189 | + } |
| 190 | +} |
0 commit comments