diff --git a/Cargo.lock b/Cargo.lock index 4f647bca32..47512af1e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3146,6 +3146,7 @@ name = "scheduler_epoch" version = "0.1.0" dependencies = [ "bit_set", + "kernel_config", "log", "spin 0.9.4", "task", diff --git a/kernel/scheduler_epoch/Cargo.toml b/kernel/scheduler_epoch/Cargo.toml index 3a877a6c23..63d7c675ae 100644 --- a/kernel/scheduler_epoch/Cargo.toml +++ b/kernel/scheduler_epoch/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bit_set = { path = "../../libs/bit_set" } +kernel_config = { path = "../kernel_config" } log = "0.4.8" spin = "0.9.4" task = { path = "../task" } diff --git a/kernel/scheduler_epoch/src/lib.rs b/kernel/scheduler_epoch/src/lib.rs index a25ed46003..40902510b9 100644 --- a/kernel/scheduler_epoch/src/lib.rs +++ b/kernel/scheduler_epoch/src/lib.rs @@ -3,6 +3,8 @@ //! The implementation is based on the [`O(1)` Linux //! scheduler][linux-scheduler]. //! +//! The scheduler is comprised of two run queues: an . +//! //! [linux-scheduler]: https://litux.nl/mirror/kerneldevelopment/0672327201/ch04lev1sec2.html #![no_std] @@ -16,6 +18,7 @@ use alloc::{boxed::Box, vec::Vec}; use core::{ mem, ops::{Deref, DerefMut}, + time::Duration, }; use task::TaskRef; @@ -24,13 +27,20 @@ use crate::queue::RunQueue; const MAX_PRIORITY: u8 = 63; const DEFAULT_PRIORITY: u8 = 20; -const INITIAL_TOKENS: usize = 0; -/// An instance of an epoch scheduler, typically one per CPU. +/// The minimum amount of time for every runnable task to run. +/// +/// This is not strictly adhered to when the tasks are run +const TARGET_LATENCY: Duration = Duration::from_millis(15); + +/// An epoch scheduler. +/// +/// See crate-level docs for more information. pub struct Scheduler { idle_task: TaskRef, active: RunQueue, expired: RunQueue, + total_weight: usize, } impl Scheduler { @@ -40,6 +50,8 @@ impl Scheduler { idle_task, active: RunQueue::new(), expired: RunQueue::new(), + // TODO: 0 or 1 + total_weight: 0, } } @@ -81,6 +93,7 @@ impl Returnable for Option { } impl task::scheduler::Scheduler for Scheduler { + #[inline] fn next(&mut self) -> TaskRef { if self.active.is_empty() { if !self.expired.is_empty() { @@ -94,23 +107,29 @@ impl task::scheduler::Scheduler for Scheduler { .unwrap_or(self.idle_task.clone()) } + #[inline] fn add(&mut self, task: TaskRef) { - let task = EpochTaskRef::new(task); + let (task, weight) = EpochTaskRef::new(task, self.total_weight); + self.total_weight += weight; self.expired.push(task, DEFAULT_PRIORITY); } + #[inline] fn busyness(&self) -> usize { self.active.len() + self.expired.len() } + #[inline] fn remove(&mut self, task: &TaskRef) -> bool { self.apply(|run_queue| run_queue.remove(task)) } + #[inline] fn as_priority_scheduler(&mut self) -> Option<&mut dyn task::scheduler::PriorityScheduler> { Some(self) } + #[inline] fn drain(&mut self) -> Box + '_> { let mut active = RunQueue::new(); let mut expired = RunQueue::new(); @@ -121,6 +140,7 @@ impl task::scheduler::Scheduler for Scheduler { Box::new(active.drain().chain(expired.drain())) } + #[inline] fn tasks(&self) -> Vec { self.active .clone() @@ -131,11 +151,13 @@ impl task::scheduler::Scheduler for Scheduler { } impl task::scheduler::PriorityScheduler for Scheduler { + #[inline] fn set_priority(&mut self, task: &TaskRef, priority: u8) -> bool { let priority = core::cmp::min(priority, MAX_PRIORITY); self.apply(|run_queue| run_queue.set_priority(task, priority)) } + #[inline] fn priority(&mut self, task: &TaskRef) -> Option { self.apply(|run_queue| run_queue.priority(task)) } @@ -147,21 +169,48 @@ struct EpochTaskRef { tokens: usize, } +impl EpochTaskRef { + #[must_use] + pub(crate) fn new(task: TaskRef, config: TaskConfiguration) -> (Self, usize) { + const NUM_TOKENS: usize = + TARGET_LATENCY / kernel_config::time::CONFIG_TIMESLICE_PERIOD_MICROSECONDS; + + // TODO + let weight = config.priority + 1; + + ( + Self { + task, + tokens: core::cmp::max(NUM_TOKENS * weight / config.total_weight, 1), + }, + weight, + ) + } +} + +pub(crate) struct TaskConfiguration { + pub(crate) priority: usize, + pub(crate) total_weight: usize, +} + impl Deref for EpochTaskRef { type Target = TaskRef; + #[inline] fn deref(&self) -> &TaskRef { &self.task } } impl DerefMut for EpochTaskRef { + #[inline] fn deref_mut(&mut self) -> &mut TaskRef { &mut self.task } } impl EpochTaskRef { + #[inline] fn new(task: TaskRef) -> EpochTaskRef { EpochTaskRef { task, @@ -171,6 +220,7 @@ impl EpochTaskRef { } impl From for TaskRef { + #[inline] fn from(value: EpochTaskRef) -> Self { value.task } diff --git a/kernel/scheduler_epoch/src/queue.rs b/kernel/scheduler_epoch/src/queue.rs index 4fdfb378b4..90fe309785 100644 --- a/kernel/scheduler_epoch/src/queue.rs +++ b/kernel/scheduler_epoch/src/queue.rs @@ -5,6 +5,9 @@ use task::TaskRef; use crate::{EpochTaskRef, MAX_PRIORITY}; +/// A singular run queue. +/// +/// The scheduler contains two of these: an active one, and an expired one. #[derive(Debug, Clone)] pub(crate) struct RunQueue { // TODO: Encode using MAX_PRIORITY @@ -14,6 +17,7 @@ pub(crate) struct RunQueue { } impl RunQueue { + #[inline] pub(crate) const fn new() -> Self { const INIT: VecDeque = VecDeque::new(); @@ -24,20 +28,25 @@ impl RunQueue { } } + #[inline] pub(crate) const fn len(&self) -> usize { + debug_assert_eq!(self.inner.iter().map(|queue| queue.len()).sum(), self.len); self.len } + #[inline] pub(crate) const fn is_empty(&self) -> bool { self.len() == 0 } + #[inline] pub(crate) fn push(&mut self, task: EpochTaskRef, priority: u8) { self.priorities.insert(priority); self.inner[priority as usize].push_back(task); self.len += 1; } + #[inline] pub(crate) fn next(&mut self, expired: &mut Self) -> Option { loop { let top_index = self.top_index()?; @@ -71,10 +80,12 @@ impl RunQueue { } } + #[inline] fn top_index(&self) -> Option { self.priorities.max().map(|priority| priority as usize) } + #[inline] pub(crate) fn remove(&mut self, task: &TaskRef) -> bool { for i in self.priorities.iter() { let queue = &mut self.inner[i]; @@ -98,6 +109,7 @@ impl RunQueue { } /// Returns the priority of the given task. + #[inline] pub(crate) fn priority(&self, task: &TaskRef) -> Option { for i in self.priorities.iter() { let queue = &self.inner[i]; @@ -114,6 +126,7 @@ impl RunQueue { /// /// Returns `true` if an action was performed i.e. if the task was in the /// run queue. + #[inline] pub(crate) fn set_priority(&mut self, task: &TaskRef, priority: u8) -> bool { for i in self.priorities.iter() { let queue = &mut self.inner[i]; @@ -137,6 +150,7 @@ impl RunQueue { false } + #[inline] pub(crate) fn drain(self) -> Drain { Drain { inner: self } }