Skip to content

Commit fc2a656

Browse files
authored
Wifi: switch tasks at interrupt level 1 (#3164)
* Pull up common code to save an instruction * Simplify * Tick at priority 1 * Rename timer handler * Explain yielding * Switch tasks at prio level 1 * Keep delegating to the SW interrupt on ESP32 * Changelog
1 parent 9bf1f33 commit fc2a656

File tree

3 files changed

+39
-29
lines changed

3 files changed

+39
-29
lines changed

esp-wifi/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Changed
1313

14+
- The scheduler now runs at interrupt priority 1 on Xtensa chips, too. (#3164)
15+
1416
### Fixed
1517

1618
### Removed

esp-wifi/src/preempt_builtin/timer/xtensa.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ const TIMESLICE_FREQUENCY: Rate = Rate::from_hz(crate::CONFIG.tick_rate_hz);
1111

1212
use super::TIMER;
1313

14+
// ESP32 uses Software1 (priority 3) for task switching, because it reserves
15+
// Software0 for the Bluetooth stack.
16+
const SW_INTERRUPT: u32 = if cfg!(esp32) { 1 << 29 } else { 1 << 7 };
17+
1418
pub(crate) fn setup_timer(mut timer1: TimeBase) {
19+
// The timer needs to tick at Priority 1 to prevent accidentally interrupting
20+
// priority 1 limited locks.
1521
timer1.set_interrupt_handler(InterruptHandler::new(
16-
unsafe { core::mem::transmute::<*const (), extern "C" fn()>(handler as *const ()) },
17-
interrupt::Priority::Priority2,
22+
unsafe {
23+
core::mem::transmute::<*const (), extern "C" fn()>(timer_tick_handler as *const ())
24+
},
25+
interrupt::Priority::Priority1,
1826
));
1927
unwrap!(timer1.start(TIMESLICE_FREQUENCY.as_duration()));
2028
TIMER.with(|timer| {
@@ -35,7 +43,7 @@ pub(crate) fn setup_multitasking() {
3543
unsafe {
3644
let enabled = xtensa_lx::interrupt::disable();
3745
xtensa_lx::interrupt::enable_mask(
38-
(1 << 29)
46+
SW_INTERRUPT
3947
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level2.mask()
4048
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level6.mask()
4149
| enabled,
@@ -44,38 +52,39 @@ pub(crate) fn setup_multitasking() {
4452
}
4553

4654
pub(crate) fn disable_multitasking() {
47-
xtensa_lx::interrupt::disable_mask(
48-
1 << 29, // Disable Software1
49-
);
55+
xtensa_lx::interrupt::disable_mask(SW_INTERRUPT);
5056
}
5157

52-
fn do_task_switch(context: &mut TrapFrame) {
58+
extern "C" fn timer_tick_handler(_context: &mut TrapFrame) {
5359
TIMER.with(|timer| {
5460
let timer = unwrap!(timer.as_mut());
5561
timer.clear_interrupt();
5662
});
5763

58-
task_switch(context);
59-
}
60-
61-
extern "C" fn handler(context: &mut TrapFrame) {
62-
do_task_switch(context);
64+
// `task_switch` must be called on a single interrupt priority level only.
65+
// Because on ESP32 the software interrupt is triggered at priority 3 but
66+
// the timer interrupt is triggered at priority 1, we need to trigger the
67+
// software interrupt manually.
68+
cfg_if::cfg_if! {
69+
if #[cfg(esp32)] {
70+
yield_task();
71+
} else {
72+
task_switch(_context);
73+
}
74+
}
6375
}
6476

6577
#[allow(non_snake_case)]
66-
#[no_mangle]
67-
fn Software1(_level: u32, context: &mut TrapFrame) {
68-
let intr = 1 << 29;
69-
unsafe {
70-
core::arch::asm!("wsr.intclear {0}", in(reg) intr, options(nostack));
71-
}
78+
#[cfg_attr(not(esp32), export_name = "Software0")]
79+
#[cfg_attr(esp32, export_name = "Software1")]
80+
fn task_switch_interrupt(_level: u32, context: &mut TrapFrame) {
81+
let intr = SW_INTERRUPT;
82+
unsafe { core::arch::asm!("wsr.intclear {0}", in(reg) intr, options(nostack)) };
7283

73-
do_task_switch(context);
84+
task_switch(context);
7485
}
7586

7687
pub(crate) fn yield_task() {
77-
let intr = 1 << 29;
78-
unsafe {
79-
core::arch::asm!("wsr.intset {0}", in(reg) intr, options(nostack));
80-
}
88+
let intr = SW_INTERRUPT;
89+
unsafe { core::arch::asm!("wsr.intset {0}", in(reg) intr, options(nostack)) };
8190
}

xtensa-lx-rt/src/exception/asm.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,16 +513,16 @@ __default_naked_exception:
513513
.Ldefault_naked_exception_start:
514514
SAVE_CONTEXT 1
515515
516+
l32i a6, sp, +XT_STK_EXCCAUSE // put cause in a6 = a2 in callee
517+
mov a7, sp // put address of save frame in a7=a3 in callee
518+
519+
beqi a6, 4, .Level1Interrupt // Handle Level1 interrupt
520+
516521
movi a0, (PS_INTLEVEL_EXCM | PS_WOE)
517522
wsr a0, PS
518523
rsync
519524
520-
l32i a6, sp, +XT_STK_EXCCAUSE // put cause in a6 = a2 in callee
521-
beqi a6, 4, .Level1Interrupt
522-
523-
mov a7, sp // put address of save frame in a7=a3 in callee
524525
call4 __exception // call handler <= actual call!
525-
526526
j .RestoreContext
527527
528528
.Level1Interrupt:
@@ -531,7 +531,6 @@ __default_naked_exception:
531531
rsync
532532
533533
movi a6, 1 // put interrupt level in a6 = a2 in callee
534-
mov a7, sp // put address of save frame in a7=a3 in callee
535534
call4 __level_1_interrupt // call handler <= actual call!
536535
537536
.RestoreContext:

0 commit comments

Comments
 (0)