From 5c8e369057e5f9607ba42d22c66540bfff20c666 Mon Sep 17 00:00:00 2001 From: jellllly420 Date: Mon, 23 Sep 2024 15:35:21 +0800 Subject: [PATCH] Enable scheduler to fetch scheduling information directly from Thread --- kernel/src/sched/priority_scheduler.rs | 142 +++++++++++----------- ostd/src/task/scheduler/fifo_scheduler.rs | 28 ++--- ostd/src/task/scheduler/info.rs | 14 +++ 3 files changed, 96 insertions(+), 88 deletions(-) diff --git a/kernel/src/sched/priority_scheduler.rs b/kernel/src/sched/priority_scheduler.rs index f84f55236..289d85945 100644 --- a/kernel/src/sched/priority_scheduler.rs +++ b/kernel/src/sched/priority_scheduler.rs @@ -4,8 +4,11 @@ use ostd::{ cpu::{num_cpus, CpuSet, PinCurrentCpu}, sync::PreemptDisabled, task::{ - scheduler::{inject_scheduler, EnqueueFlags, LocalRunQueue, Scheduler, UpdateFlags}, - AtomicCpuId, Task, + scheduler::{ + info::CommonSchedInfo, inject_scheduler, EnqueueFlags, LocalRunQueue, Scheduler, + UpdateFlags, + }, + Task, }, trap::disable_local, }; @@ -15,7 +18,7 @@ use crate::{prelude::*, thread::Thread}; pub fn init() { let preempt_scheduler = Box::new(PreemptScheduler::default()); - let scheduler = Box::>::leak(preempt_scheduler); + let scheduler = Box::>::leak(preempt_scheduler); inject_scheduler(scheduler); } @@ -25,11 +28,11 @@ pub fn init() { /// are always prioritized during scheduling. /// Normal tasks are placed in the `normal_entities` queue and are only /// scheduled for execution when there are no real-time tasks. -struct PreemptScheduler { - rq: Vec>>, +struct PreemptScheduler, U: CommonSchedInfo> { + rq: Vec>>, } -impl PreemptScheduler { +impl, U: CommonSchedInfo> PreemptScheduler { fn new(nr_cpus: u32) -> Self { let mut rq = Vec::with_capacity(nr_cpus as usize); for _ in 0..nr_cpus { @@ -39,11 +42,11 @@ impl PreemptScheduler { } /// Selects a CPU for task to run on for the first time. - fn select_cpu(&self, runnable: &Arc) -> u32 { + fn select_cpu(&self, entity: &PreemptSchedEntity) -> u32 { // If the CPU of a runnable task has been set before, keep scheduling // the task to that one. // TODO: Consider migrating tasks between CPUs for load balancing. - if let Some(cpu_id) = runnable.cpu().get() { + if let Some(cpu_id) = entity.task.cpu().get() { return cpu_id; } @@ -51,7 +54,7 @@ impl PreemptScheduler { let mut selected = irq_guard.current_cpu(); let mut minimum_load = usize::MAX; - for candidate in runnable.cpu_affinity().iter() { + for candidate in entity.thread.cpu_affinity().iter() { let rq = self.rq[candidate as usize].lock(); // A wild guess measuring the load of a runqueue. We assume that // real-time tasks are 4-times as important as normal tasks. @@ -68,12 +71,15 @@ impl PreemptScheduler { } } -impl Scheduler for PreemptScheduler { - fn enqueue(&self, runnable: Arc, flags: EnqueueFlags) -> Option { +impl, U: Sync + Send + CommonSchedInfo> Scheduler + for PreemptScheduler +{ + fn enqueue(&self, task: Arc, flags: EnqueueFlags) -> Option { + let entity = PreemptSchedEntity::new(task); let mut still_in_rq = false; let target_cpu = { - let mut cpu_id = self.select_cpu(&runnable); - if let Err(task_cpu_id) = runnable.cpu().set_if_is_none(cpu_id) { + let mut cpu_id = self.select_cpu(&entity); + if let Err(task_cpu_id) = entity.task.cpu().set_if_is_none(cpu_id) { debug_assert!(flags != EnqueueFlags::Spawn); still_in_rq = true; cpu_id = task_cpu_id; @@ -83,13 +89,12 @@ impl Scheduler for PreemptScheduler { }; let mut rq = self.rq[target_cpu as usize].disable_irq().lock(); - if still_in_rq && let Err(_) = runnable.cpu().set_if_is_none(target_cpu) { + if still_in_rq && let Err(_) = entity.task.cpu().set_if_is_none(target_cpu) { return None; } - let entity = PreemptSchedEntity::new(runnable); - if entity.is_real_time() { + if entity.thread.is_real_time() { rq.real_time_entities.push_back(entity); - } else if entity.is_lowest() { + } else if entity.thread.is_lowest() { rq.lowest_entities.push_back(entity); } else { rq.normal_entities.push_back(entity); @@ -98,34 +103,34 @@ impl Scheduler for PreemptScheduler { Some(target_cpu) } - fn local_rq_with(&self, f: &mut dyn FnMut(&dyn LocalRunQueue)) { + fn local_rq_with(&self, f: &mut dyn FnMut(&dyn LocalRunQueue)) { let irq_guard = disable_local(); - let local_rq: &PreemptRunQueue = &self.rq[irq_guard.current_cpu() as usize].lock(); + let local_rq: &PreemptRunQueue = &self.rq[irq_guard.current_cpu() as usize].lock(); f(local_rq); } - fn local_mut_rq_with(&self, f: &mut dyn FnMut(&mut dyn LocalRunQueue)) { + fn local_mut_rq_with(&self, f: &mut dyn FnMut(&mut dyn LocalRunQueue)) { let irq_guard = disable_local(); - let local_rq: &mut PreemptRunQueue = + let local_rq: &mut PreemptRunQueue = &mut self.rq[irq_guard.current_cpu() as usize].lock(); f(local_rq); } } -impl Default for PreemptScheduler { +impl Default for PreemptScheduler { fn default() -> Self { Self::new(num_cpus()) } } -struct PreemptRunQueue { - current: Option>, - real_time_entities: VecDeque>, - normal_entities: VecDeque>, - lowest_entities: VecDeque>, +struct PreemptRunQueue, U: CommonSchedInfo> { + current: Option>, + real_time_entities: VecDeque>, + normal_entities: VecDeque>, + lowest_entities: VecDeque>, } -impl PreemptRunQueue { +impl, U: CommonSchedInfo> PreemptRunQueue { pub fn new() -> Self { Self { current: None, @@ -136,9 +141,11 @@ impl PreemptRunQueue { } } -impl LocalRunQueue for PreemptRunQueue { - fn current(&self) -> Option<&Arc> { - self.current.as_ref().map(|entity| &entity.runnable) +impl, U: CommonSchedInfo> LocalRunQueue + for PreemptRunQueue +{ + fn current(&self) -> Option<&Arc> { + self.current.as_ref().map(|entity| &entity.task) } fn update_current(&mut self, flags: UpdateFlags) -> bool { @@ -148,13 +155,14 @@ impl LocalRunQueue for PreemptRunQueue return false; }; current_entity.tick() - || (!current_entity.is_real_time() && !self.real_time_entities.is_empty()) + || (!current_entity.thread.is_real_time() + && !self.real_time_entities.is_empty()) } _ => true, } } - fn pick_next_current(&mut self) -> Option<&Arc> { + fn pick_next_current(&mut self) -> Option<&Arc> { let next_entity = if !self.real_time_entities.is_empty() { self.real_time_entities.pop_front() } else if !self.normal_entities.is_empty() { @@ -163,58 +171,54 @@ impl LocalRunQueue for PreemptRunQueue self.lowest_entities.pop_front() }?; if let Some(prev_entity) = self.current.replace(next_entity) { - if prev_entity.is_real_time() { + if prev_entity.thread.is_real_time() { self.real_time_entities.push_back(prev_entity); - } else if prev_entity.is_lowest() { + } else if prev_entity.thread.is_lowest() { self.lowest_entities.push_back(prev_entity); } else { self.normal_entities.push_back(prev_entity); } } - Some(&self.current.as_ref().unwrap().runnable) + Some(&self.current.as_ref().unwrap().task) } - fn dequeue_current(&mut self) -> Option> { + fn dequeue_current(&mut self) -> Option> { self.current.take().map(|entity| { - let runnable = entity.runnable; + let runnable = entity.task; runnable.cpu().set_to_none(); runnable }) } } - -struct PreemptSchedEntity { - runnable: Arc, +struct PreemptSchedEntity, U: CommonSchedInfo> { + task: Arc, + thread: Arc, time_slice: TimeSlice, } -impl PreemptSchedEntity { - fn new(runnable: Arc) -> Self { +impl, U: CommonSchedInfo> PreemptSchedEntity { + fn new(task: Arc) -> Self { + let thread = T::from_task(&task); + let time_slice = TimeSlice::default(); Self { - runnable, - time_slice: TimeSlice::default(), + task, + thread, + time_slice, } } - fn is_real_time(&self) -> bool { - self.runnable.is_real_time() - } - - fn is_lowest(&self) -> bool { - self.runnable.is_lowest() - } - fn tick(&mut self) -> bool { self.time_slice.elapse() } } -impl Clone for PreemptSchedEntity { +impl, U: CommonSchedInfo> Clone for PreemptSchedEntity { fn clone(&self) -> Self { Self { - runnable: self.runnable.clone(), + task: self.task.clone(), + thread: self.thread.clone(), time_slice: self.time_slice, } } @@ -245,26 +249,16 @@ impl Default for TimeSlice { } } -impl PreemptSchedInfo for Task { +impl PreemptSchedInfo for Thread { const REAL_TIME_TASK_PRIORITY: Priority = Priority::new(PriorityRange::new(100)); const LOWEST_TASK_PRIORITY: Priority = Priority::new(PriorityRange::new(PriorityRange::MAX)); fn priority(&self) -> Priority { - self.data() - .downcast_ref::>() - .unwrap() - .priority() - } - - fn cpu(&self) -> &AtomicCpuId { - &self.schedule_info().cpu + self.priority() } fn cpu_affinity(&self) -> SpinLockGuard { - self.data() - .downcast_ref::>() - .unwrap() - .lock_cpu_affinity() + self.lock_cpu_affinity() } } @@ -274,8 +268,6 @@ trait PreemptSchedInfo { fn priority(&self) -> Priority; - fn cpu(&self) -> &AtomicCpuId; - fn cpu_affinity(&self) -> SpinLockGuard; fn is_real_time(&self) -> bool { @@ -286,3 +278,13 @@ trait PreemptSchedInfo { self.priority() == Self::LOWEST_TASK_PRIORITY } } + +impl FromTask for Thread { + fn from_task(task: &Arc) -> Arc { + task.data().downcast_ref::>().unwrap().clone() + } +} + +trait FromTask { + fn from_task(task: &Arc) -> Arc; +} diff --git a/ostd/src/task/scheduler/fifo_scheduler.rs b/ostd/src/task/scheduler/fifo_scheduler.rs index 3b0976778..28121e69c 100644 --- a/ostd/src/task/scheduler/fifo_scheduler.rs +++ b/ostd/src/task/scheduler/fifo_scheduler.rs @@ -2,11 +2,13 @@ use alloc::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec}; -use super::{inject_scheduler, EnqueueFlags, LocalRunQueue, Scheduler, UpdateFlags}; +use super::{ + info::CommonSchedInfo, inject_scheduler, EnqueueFlags, LocalRunQueue, Scheduler, UpdateFlags, +}; use crate::{ cpu::{num_cpus, PinCurrentCpu}, sync::SpinLock, - task::{disable_preempt, AtomicCpuId, Task}, + task::{disable_preempt, Task}, }; pub fn init() { @@ -16,12 +18,12 @@ pub fn init() { } /// A simple FIFO (First-In-First-Out) task scheduler. -struct FifoScheduler { +struct FifoScheduler { /// A thread-safe queue to hold tasks waiting to be executed. rq: Vec>>, } -impl FifoScheduler { +impl FifoScheduler { /// Creates a new instance of `FifoScheduler`. fn new(nr_cpus: u32) -> Self { let mut rq = Vec::new(); @@ -37,7 +39,7 @@ impl FifoScheduler { } } -impl Scheduler for FifoScheduler { +impl Scheduler for FifoScheduler { fn enqueue(&self, runnable: Arc, flags: EnqueueFlags) -> Option { let mut still_in_rq = false; let target_cpu = { @@ -77,12 +79,12 @@ impl Scheduler for FifoScheduler { } } -struct FifoRunQueue { +struct FifoRunQueue { current: Option>, queue: VecDeque>, } -impl FifoRunQueue { +impl FifoRunQueue { pub const fn new() -> Self { Self { current: None, @@ -91,7 +93,7 @@ impl FifoRunQueue { } } -impl LocalRunQueue for FifoRunQueue { +impl LocalRunQueue for FifoRunQueue { fn current(&self) -> Option<&Arc> { self.current.as_ref() } @@ -119,13 +121,3 @@ impl Default for FifoScheduler { Self::new(num_cpus()) } } - -impl FifoSchedInfo for Task { - fn cpu(&self) -> &AtomicCpuId { - &self.schedule_info().cpu - } -} - -trait FifoSchedInfo { - fn cpu(&self) -> &AtomicCpuId; -} diff --git a/ostd/src/task/scheduler/info.rs b/ostd/src/task/scheduler/info.rs index 8b45f6c05..f804613b8 100644 --- a/ostd/src/task/scheduler/info.rs +++ b/ostd/src/task/scheduler/info.rs @@ -4,6 +4,8 @@ use core::sync::atomic::{AtomicU32, Ordering}; +use crate::task::Task; + /// Fields of a task that OSTD will never touch. /// /// The type ought to be defined by the OSTD user and injected into the task. @@ -61,3 +63,15 @@ impl Default for AtomicCpuId { Self::new(Self::NONE) } } + +impl CommonSchedInfo for Task { + fn cpu(&self) -> &AtomicCpuId { + &self.schedule_info().cpu + } +} + +/// Trait for fetching common scheduling information. +pub trait CommonSchedInfo { + /// Gets the CPU that the task is running on or lately ran on. + fn cpu(&self) -> &AtomicCpuId; +}