11//! A GEDF scheduler.
22
3+ use core:: cmp:: max;
4+
35use super :: { Scheduler , SchedulerType , Task } ;
46use crate :: {
5- scheduler:: get_priority,
7+ scheduler:: { get_priority, peek_preemption_pending , push_preemption_pending } ,
68 task:: {
7- get_absolute_deadline_by_task_id , get_tasks_running, set_need_preemption, State ,
9+ get_task , get_tasks_running, set_current_task , set_need_preemption, State ,
810 MAX_TASK_PRIORITY ,
911 } ,
1012} ;
11- use alloc:: { collections:: BinaryHeap , sync:: Arc } ;
12- use awkernel_lib:: {
13- cpu:: num_cpu,
14- sync:: mutex:: { MCSNode , Mutex } ,
15- } ;
13+ use alloc:: { collections:: BinaryHeap , sync:: Arc , vec:: Vec } ;
14+ use awkernel_lib:: sync:: mutex:: { MCSNode , Mutex } ;
1615
1716pub struct GEDFScheduler {
1817 data : Mutex < Option < GEDFData > > , // Run queue.
@@ -63,30 +62,36 @@ impl GEDFData {
6362impl Scheduler for GEDFScheduler {
6463 fn wake_task ( & self , task : Arc < Task > ) {
6564 let mut node = MCSNode :: new ( ) ;
65+ // The reason for acquiring this lock before invoke_preemption() is to prevent priority inversion from occurring
66+ // when invoke_preemption() is executed between the time the next task is determined and the RUNNING is updated
67+ // within the scheduler's get_next().
6668 let mut data = self . data . lock ( & mut node) ;
67- let data = data. get_or_insert_with ( GEDFData :: new) ;
68-
69- let mut node = MCSNode :: new ( ) ;
70- let mut info = task. info . lock ( & mut node) ;
71-
72- let SchedulerType :: GEDF ( relative_deadline) = info. scheduler_type else {
73- unreachable ! ( ) ;
69+ let internal_data = data. get_or_insert_with ( GEDFData :: new) ;
70+
71+ let ( wake_time, absolute_deadline) = {
72+ let mut node_inner = MCSNode :: new ( ) ;
73+ let mut info = task. info . lock ( & mut node_inner) ;
74+ match info. scheduler_type {
75+ SchedulerType :: GEDF ( relative_deadline) => {
76+ let wake_time = awkernel_lib:: delay:: uptime ( ) ;
77+ let absolute_deadline = wake_time + relative_deadline;
78+ task. priority
79+ . update_priority_info ( self . priority , MAX_TASK_PRIORITY - absolute_deadline) ;
80+ info. update_absolute_deadline ( absolute_deadline) ;
81+
82+ ( wake_time, absolute_deadline)
83+ }
84+ _ => unreachable ! ( ) ,
85+ }
7486 } ;
7587
76- let wake_time = awkernel_lib:: delay:: uptime ( ) ;
77- let absolute_deadline = wake_time + relative_deadline;
78-
79- task. priority
80- . update_priority_info ( self . priority , MAX_TASK_PRIORITY - absolute_deadline) ;
81- info. update_absolute_deadline ( absolute_deadline) ;
82-
83- data. queue . push ( GEDFTask {
84- task : task. clone ( ) ,
85- absolute_deadline,
86- wake_time,
87- } ) ;
88-
89- self . invoke_preemption ( absolute_deadline) ;
88+ if !self . invoke_preemption ( task. clone ( ) ) {
89+ internal_data. queue . push ( GEDFTask {
90+ task : task. clone ( ) ,
91+ absolute_deadline,
92+ wake_time,
93+ } ) ;
94+ }
9095 }
9196
9297 fn get_next ( & self ) -> Option < Arc < Task > > {
@@ -116,6 +121,7 @@ impl Scheduler for GEDFScheduler {
116121 task_info. need_preemption = false ;
117122 }
118123 task_info. state = State :: Running ;
124+ set_current_task ( awkernel_lib:: cpu:: cpu_id ( ) , task. task . id ) ;
119125 }
120126
121127 return Some ( task. task ) ;
@@ -137,30 +143,42 @@ pub static SCHEDULER: GEDFScheduler = GEDFScheduler {
137143} ;
138144
139145impl GEDFScheduler {
140- fn invoke_preemption ( & self , absolute_deadline : u64 ) {
141- // Get running tasks and filter out tasks with task_id == 0.
142- let mut tasks = get_tasks_running ( ) ;
143- tasks . retain ( |task| task . task_id != 0 ) ;
144-
145- // If the number of running tasks is less than the number of non-primary CPUs, preempt is not required.
146- let num_non_primary_cpus = num_cpu ( ) - 1 ;
147- if tasks . len ( ) < num_non_primary_cpus {
148- return ;
146+ fn invoke_preemption ( & self , task : Arc < Task > ) -> bool {
147+ let tasks_running = get_tasks_running ( )
148+ . into_iter ( )
149+ . filter ( |rt| rt . task_id != 0 ) // Filter out idle CPUs
150+ . collect :: < Vec < _ > > ( ) ;
151+
152+ // If the task has already been running, preempt is not required.
153+ if tasks_running . is_empty ( ) || tasks_running . iter ( ) . any ( |rt| rt . task_id == task . id ) {
154+ return false ;
149155 }
150156
151- let task_with_max_deadline = tasks
157+ let preemption_target = tasks_running
152158 . iter ( )
153- . filter_map ( |task| {
154- get_absolute_deadline_by_task_id ( task. task_id ) . map ( |deadline| ( task, deadline) )
159+ . filter_map ( |rt| {
160+ get_task ( rt. task_id ) . map ( |t| {
161+ let highest_pending = peek_preemption_pending ( rt. cpu_id ) . unwrap_or ( t. clone ( ) ) ;
162+ ( max ( t, highest_pending) , rt. cpu_id )
163+ } )
155164 } )
156- . max_by_key ( |& ( _, deadline) | deadline) ;
165+ . min ( )
166+ . unwrap ( ) ;
157167
158- if let Some ( ( task, max_absolute_deadline) ) = task_with_max_deadline {
159- if max_absolute_deadline > absolute_deadline {
160- let preempt_irq = awkernel_lib:: interrupt:: get_preempt_irq ( ) ;
161- set_need_preemption ( task. task_id , task. cpu_id ) ;
162- awkernel_lib:: interrupt:: send_ipi ( preempt_irq, task. cpu_id as u32 ) ;
163- }
168+ let ( target_task, target_cpu) = preemption_target;
169+ if task > target_task {
170+ push_preemption_pending ( target_cpu, task) ;
171+ let preempt_irq = awkernel_lib:: interrupt:: get_preempt_irq ( ) ;
172+ set_need_preemption ( target_task. id , target_cpu) ;
173+ awkernel_lib:: interrupt:: send_ipi ( preempt_irq, target_cpu as u32 ) ;
174+
175+ // NOTE(atsushi421): Currently, preemption is requested regardless of the number of idle CPUs.
176+ // While this implementation easily prevents priority inversion, it may also cause unnecessary preemption.
177+ // Therefore, a more sophisticated implementation will be considered in the future.
178+
179+ return true ;
164180 }
181+
182+ false
165183 }
166184}
0 commit comments