Skip to content

Commit 09539b5

Browse files
authored
Merge pull request #635 from tier4/fix_priority_inversion_gedf
fix(GEDFScheduler): solve inter-scheduler priority inversion
2 parents 264fb94 + 8d468e3 commit 09539b5

File tree

2 files changed

+65
-59
lines changed

2 files changed

+65
-59
lines changed

awkernel_async_lib/src/scheduler/gedf.rs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
//! A GEDF scheduler.
22
3+
use core::cmp::max;
4+
35
use super::{Scheduler, SchedulerType, Task};
46
use 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

1716
pub struct GEDFScheduler {
1817
data: Mutex<Option<GEDFData>>, // Run queue.
@@ -63,30 +62,36 @@ impl GEDFData {
6362
impl 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

139145
impl 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
}

awkernel_async_lib/src/task.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -942,18 +942,6 @@ pub fn get_scheduler_type_by_task_id(task_id: u32) -> Option<SchedulerType> {
942942
})
943943
}
944944

945-
#[inline(always)]
946-
pub fn get_absolute_deadline_by_task_id(task_id: u32) -> Option<u64> {
947-
let mut node = MCSNode::new();
948-
let tasks = TASKS.lock(&mut node);
949-
950-
tasks.id_to_task.get(&task_id).and_then(|task| {
951-
let mut node = MCSNode::new();
952-
let info = task.info.lock(&mut node);
953-
info.get_absolute_deadline()
954-
})
955-
}
956-
957945
#[inline(always)]
958946
pub fn set_need_preemption(task_id: u32, cpu_id: usize) {
959947
let mut node = MCSNode::new();

0 commit comments

Comments
 (0)