mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
backport: fix ceiling analysis bug
This commit fixes a ceiling bug where the ceiling of a ready queue will be incorrectly computed. The analysis was not including the priority of the system timer interrupt (`SysTick`) in the analysis resulting in a priority ceiling lower than what's required for memory safety which led to data races. The bug can be observed in the following program: ``` rust #[rtfm::app(device = /* .. */)] const APP: () = { #[init] fn init() { // .. } #[task(priority = 2)] fn foo(x: i32) { // .. } #[task(priority = 1, spawn = [foo], schedule = [foo])] fn bar() { // .. } extern "C" { fn EXTI0(); fn EXTI1(); } }; ``` Here the framework chooses a priority of `2` for the `SysTick` interrupt (because it matches the priority of the `schedule`-able task `foo`). Both `SysTick` and `bar::Spawn.foo` need to access the ready queue (which, in this case, stores the messages sent to task `foo`) but the framework doesn't account for the priority of `SysTick` (`2`) and chooses a priority ceiling of `1` for the ready queue (because it matches the priority of task `bar` which can spawn `foo`). The result is that `bar::Spawn.foo` modifies the ready queue *without* a critical section (because `bar`'s priority matches the priority ceiling of the ready queue) which is wrong because `SysTick` (priority = `3`) can also modify the ready queue.
This commit is contained in:
parent
9b9a80d38e
commit
7da8463980
1 changed files with 8 additions and 1 deletions
|
@ -190,7 +190,7 @@ pub fn app(app: &App) -> Analysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ceiling analysis of free queues (consumer end point) -- first pass
|
// Ceiling analysis of free queues (consumer end point) -- first pass
|
||||||
// Ceiling analysis of ready queues (producer end point)
|
// Ceiling analysis of ready queues (producer end point) -- first pass
|
||||||
// Also compute more Send-ness requirements
|
// Also compute more Send-ness requirements
|
||||||
let mut free_queues: HashMap<_, _> = app.tasks.keys().map(|task| (task.clone(), 0)).collect();
|
let mut free_queues: HashMap<_, _> = app.tasks.keys().map(|task| (task.clone(), 0)).collect();
|
||||||
let mut ready_queues: HashMap<_, _> = dispatchers.keys().map(|level| (*level, 0)).collect();
|
let mut ready_queues: HashMap<_, _> = dispatchers.keys().map(|level| (*level, 0)).collect();
|
||||||
|
@ -215,10 +215,17 @@ pub fn app(app: &App) -> Analysis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ceiling analysis of ready queues (producer end point) -- second pass
|
||||||
// Ceiling analysis of free queues (consumer end point) -- second pass
|
// Ceiling analysis of free queues (consumer end point) -- second pass
|
||||||
// Ceiling analysis of the timer queue
|
// Ceiling analysis of the timer queue
|
||||||
let mut tq_ceiling = tq_priority;
|
let mut tq_ceiling = tq_priority;
|
||||||
for (priority, task) in app.schedule_calls() {
|
for (priority, task) in app.schedule_calls() {
|
||||||
|
// the system timer handler contends for the spawnee's dispatcher READY_QUEUE
|
||||||
|
let c = ready_queues
|
||||||
|
.entry(app.tasks[task].args.priority)
|
||||||
|
.or_default();
|
||||||
|
*c = cmp::max(*c, tq_priority);
|
||||||
|
|
||||||
if let Some(priority) = priority {
|
if let Some(priority) = priority {
|
||||||
// Users of `schedule` contend for the to-be-spawned task FREE_QUEUE (consumer end point)
|
// Users of `schedule` contend for the to-be-spawned task FREE_QUEUE (consumer end point)
|
||||||
let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut");
|
let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut");
|
||||||
|
|
Loading…
Reference in a new issue