Skip to content

Commit 3adfbea

Browse files
bugadanibjoernQ
authored andcommitted
Avoid AtomicU64 if possible, fix race condition (esp-rs#278)
* Xtensa: avoid AtomicU64 in get_systimer_count * Fix get_systimer_count racy behaviour * Improve code consistency
1 parent 5f02512 commit 3adfbea

File tree

4 files changed

+67
-44
lines changed

4 files changed

+67
-44
lines changed

esp-wifi/src/tasks.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ pub extern "C" fn worker_task2() {
3535
loop {
3636
let mut to_run = SimpleQueue::<_, 20>::new();
3737

38+
let current_timestamp = get_systimer_count();
3839
critical_section::with(|_| unsafe {
39-
let current_timestamp = get_systimer_count();
4040
memory_fence();
4141
for i in 0..TIMERS.len() {
4242
if let Some(ref mut timer) = TIMERS[i] {

esp-wifi/src/timer_esp32.rs

+22-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
use core::cell::RefCell;
22

3-
use atomic_polyfill::{AtomicU64, Ordering};
3+
use atomic_polyfill::{AtomicU32, Ordering};
44
use critical_section::Mutex;
5-
use esp32_hal::xtensa_lx;
6-
use esp32_hal::xtensa_lx_rt;
7-
use esp32_hal::xtensa_lx_rt::exception::Context;
5+
use esp32_hal::trapframe::TrapFrame;
86
use esp32_hal::{
97
interrupt,
108
peripherals::{self, TIMG1},
119
prelude::*,
1210
timer::{Timer, Timer0},
11+
xtensa_lx, xtensa_lx_rt,
1312
};
1413

1514
use crate::preempt::preempt::task_switch;
@@ -23,16 +22,24 @@ const TIMER_DELAY: fugit::HertzU32 = fugit::HertzU32::from_raw(crate::CONFIG.tic
2322

2423
static TIMER1: Mutex<RefCell<Option<Timer<Timer0<TIMG1>>>>> = Mutex::new(RefCell::new(None));
2524

26-
static TIME: AtomicU64 = AtomicU64::new(0);
25+
static TIMER_OVERFLOWS: AtomicU32 = AtomicU32::new(0);
2726

27+
/// This function must not be called in a critical section. Doing so may return an incorrect value.
2828
pub fn get_systimer_count() -> u64 {
29-
TIME.load(Ordering::Relaxed) + read_timer_value()
30-
}
29+
// We read the cycle counter twice to detect overflows.
30+
// If we don't detect an overflow, we use the TIMER_OVERFLOWS count we read between.
31+
// If we detect an overflow, we read the TIMER_OVERFLOWS count again to make sure we use the
32+
// value after the overflow has been handled.
33+
34+
let counter_before = xtensa_lx::timer::get_cycle_count();
35+
let mut overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
36+
let counter_after = xtensa_lx::timer::get_cycle_count();
37+
38+
if counter_after < counter_before {
39+
overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
40+
}
3141

32-
#[inline(always)]
33-
fn read_timer_value() -> u64 {
34-
let value = xtensa_lx::timer::get_cycle_count() as u64;
35-
value * 40_000_000 / 240_000_000
42+
(((overflow as u64) << 32) + counter_after as u64) * 40_000_000 / 240_000_000
3643
}
3744

3845
pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
@@ -74,7 +81,7 @@ pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
7481
xtensa_lx::timer::set_ccompare0(0xffffffff);
7582

7683
unsafe {
77-
let enabled = esp32_hal::xtensa_lx::interrupt::disable();
84+
let enabled = xtensa_lx::interrupt::disable();
7885
xtensa_lx::interrupt::enable_mask(
7986
1 << 6 // Timer0
8087
| 1 << 29 // Software1
@@ -104,7 +111,7 @@ fn Software0(_level: u32) {
104111
#[allow(non_snake_case)]
105112
#[no_mangle]
106113
fn Timer0(_level: u32) {
107-
TIME.fetch_add(0x1_0000_0000 * 40_000_000 / 240_000_000, Ordering::Relaxed);
114+
TIMER_OVERFLOWS.fetch_add(1, Ordering::Relaxed);
108115

109116
xtensa_lx::timer::set_ccompare0(0xffffffff);
110117
}
@@ -166,7 +173,7 @@ fn BT_BB() {
166173
}
167174

168175
#[interrupt]
169-
fn TG1_T0_LEVEL(context: &mut Context) {
176+
fn TG1_T0_LEVEL(context: &mut TrapFrame) {
170177
task_switch(context);
171178

172179
critical_section::with(|cs| {
@@ -181,7 +188,7 @@ fn TG1_T0_LEVEL(context: &mut Context) {
181188

182189
#[allow(non_snake_case)]
183190
#[no_mangle]
184-
fn Software1(_level: u32, context: &mut Context) {
191+
fn Software1(_level: u32, context: &mut TrapFrame) {
185192
let intr = 1 << 29;
186193
unsafe {
187194
core::arch::asm!("wsr.intclear {0}", in(reg) intr, options(nostack));

esp-wifi/src/timer_esp32s2.rs

+22-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
use core::cell::RefCell;
22

3-
use atomic_polyfill::{AtomicU64, Ordering};
3+
use atomic_polyfill::{AtomicU32, Ordering};
44
use critical_section::Mutex;
5-
use esp32s2_hal::xtensa_lx;
6-
use esp32s2_hal::xtensa_lx_rt;
7-
use esp32s2_hal::xtensa_lx_rt::exception::Context;
5+
use esp32s2_hal::trapframe::TrapFrame;
86
use esp32s2_hal::{
97
interrupt,
108
peripherals::{self, TIMG1},
119
prelude::*,
1210
timer::{Timer, Timer0},
11+
xtensa_lx, xtensa_lx_rt,
1312
};
1413

1514
use crate::preempt::preempt::task_switch;
@@ -23,16 +22,24 @@ const TIMER_DELAY: fugit::HertzU32 = fugit::HertzU32::from_raw(crate::CONFIG.tic
2322

2423
static TIMER1: Mutex<RefCell<Option<Timer<Timer0<TIMG1>>>>> = Mutex::new(RefCell::new(None));
2524

26-
static TIME: AtomicU64 = AtomicU64::new(0);
25+
static TIMER_OVERFLOWS: AtomicU32 = AtomicU32::new(0);
2726

27+
/// This function must not be called in a critical section. Doing so may return an incorrect value.
2828
pub fn get_systimer_count() -> u64 {
29-
TIME.load(Ordering::Relaxed) + read_timer_value()
30-
}
29+
// We read the cycle counter twice to detect overflows.
30+
// If we don't detect an overflow, we use the TIMER_OVERFLOWS count we read between.
31+
// If we detect an overflow, we read the TIMER_OVERFLOWS count again to make sure we use the
32+
// value after the overflow has been handled.
33+
34+
let counter_before = xtensa_lx::timer::get_cycle_count();
35+
let mut overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
36+
let counter_after = xtensa_lx::timer::get_cycle_count();
37+
38+
if counter_after < counter_before {
39+
overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
40+
}
3141

32-
#[inline(always)]
33-
fn read_timer_value() -> u64 {
34-
let value = xtensa_lx::timer::get_cycle_count() as u64;
35-
value * 40_000_000 / 240_000_000
42+
(((overflow as u64) << 32) + counter_after as u64) * 40_000_000 / 240_000_000
3643
}
3744

3845
pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
@@ -63,7 +70,7 @@ pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
6370
xtensa_lx::timer::set_ccompare0(0xffffffff);
6471

6572
unsafe {
66-
let enabled = esp32s2_hal::xtensa_lx::interrupt::disable();
73+
let enabled = xtensa_lx::interrupt::disable();
6774
xtensa_lx::interrupt::enable_mask(
6875
1 << 6 // Timer0
6976
| 1 << 29 // Software1
@@ -78,7 +85,7 @@ pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
7885
#[allow(non_snake_case)]
7986
#[no_mangle]
8087
fn Timer0(_level: u32) {
81-
TIME.fetch_add(0x1_0000_0000 * 40_000_000 / 240_000_000, Ordering::Relaxed);
88+
TIMER_OVERFLOWS.fetch_add(1, Ordering::Relaxed);
8289

8390
xtensa_lx::timer::set_ccompare0(0xffffffff);
8491
}
@@ -115,7 +122,7 @@ fn WIFI_PWR() {
115122
}
116123

117124
#[interrupt]
118-
fn TG1_T0_LEVEL(context: &mut Context) {
125+
fn TG1_T0_LEVEL(context: &mut TrapFrame) {
119126
task_switch(context);
120127

121128
critical_section::with(|cs| {
@@ -130,7 +137,7 @@ fn TG1_T0_LEVEL(context: &mut Context) {
130137

131138
#[allow(non_snake_case)]
132139
#[no_mangle]
133-
fn Software1(_level: u32, context: &mut Context) {
140+
fn Software1(_level: u32, context: &mut TrapFrame) {
134141
let intr = 1 << 29;
135142
unsafe {
136143
core::arch::asm!("wsr.intclear {0}", in(reg) intr, options(nostack));

esp-wifi/src/timer_esp32s3.rs

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use core::cell::RefCell;
22

3-
use atomic_polyfill::{AtomicU64, Ordering};
3+
use atomic_polyfill::{AtomicU32, Ordering};
44
use critical_section::Mutex;
55
use esp32s3_hal::trapframe::TrapFrame;
66
use esp32s3_hal::{
77
interrupt,
88
peripherals::{self, TIMG1},
99
prelude::*,
1010
timer::{Timer, Timer0},
11+
xtensa_lx, xtensa_lx_rt,
1112
};
1213

1314
use crate::preempt::preempt::task_switch;
@@ -21,16 +22,24 @@ const TIMER_DELAY: fugit::HertzU32 = fugit::HertzU32::from_raw(crate::CONFIG.tic
2122

2223
static TIMER1: Mutex<RefCell<Option<Timer<Timer0<TIMG1>>>>> = Mutex::new(RefCell::new(None));
2324

24-
static TIME: AtomicU64 = AtomicU64::new(0);
25+
static TIMER_OVERFLOWS: AtomicU32 = AtomicU32::new(0);
2526

27+
/// This function must not be called in a critical section. Doing so may return an incorrect value.
2628
pub fn get_systimer_count() -> u64 {
27-
TIME.load(Ordering::Relaxed) + read_timer_value()
28-
}
29+
// We read the cycle counter twice to detect overflows.
30+
// If we don't detect an overflow, we use the TIMER_OVERFLOWS count we read between.
31+
// If we detect an overflow, we read the TIMER_OVERFLOWS count again to make sure we use the
32+
// value after the overflow has been handled.
33+
34+
let counter_before = xtensa_lx::timer::get_cycle_count();
35+
let mut overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
36+
let counter_after = xtensa_lx::timer::get_cycle_count();
37+
38+
if counter_after < counter_before {
39+
overflow = TIMER_OVERFLOWS.load(Ordering::Relaxed);
40+
}
2941

30-
#[inline(always)]
31-
fn read_timer_value() -> u64 {
32-
let value = esp32s3_hal::xtensa_lx::timer::get_cycle_count() as u64;
33-
value * 40_000_000 / 240_000_000
42+
(((overflow as u64) << 32) + counter_after as u64) * 40_000_000 / 240_000_000
3443
}
3544

3645
pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
@@ -73,12 +82,12 @@ pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
7382
esp32s3_hal::xtensa_lx::timer::set_ccompare0(0xffffffff);
7483

7584
unsafe {
76-
let enabled = esp32s3_hal::xtensa_lx::interrupt::disable();
77-
esp32s3_hal::xtensa_lx::interrupt::enable_mask(
85+
let enabled = xtensa_lx::interrupt::disable();
86+
xtensa_lx::interrupt::enable_mask(
7887
1 << 6 // Timer0
7988
| 1 << 29 // Software1
80-
| esp32s3_hal::xtensa_lx_rt::interrupt::CpuInterruptLevel::Level2.mask()
81-
| esp32s3_hal::xtensa_lx_rt::interrupt::CpuInterruptLevel::Level6.mask() | enabled,
89+
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level2.mask()
90+
| xtensa_lx_rt::interrupt::CpuInterruptLevel::Level6.mask() | enabled,
8291
);
8392
}
8493

@@ -88,7 +97,7 @@ pub fn setup_timer_isr(timg1_timer0: Timer<Timer0<TIMG1>>) {
8897
#[allow(non_snake_case)]
8998
#[no_mangle]
9099
fn Timer0(_level: u32) {
91-
TIME.fetch_add(0x1_0000_0000 * 40_000_000 / 240_000_000, Ordering::Relaxed);
100+
TIMER_OVERFLOWS.fetch_add(1, Ordering::Relaxed);
92101

93102
esp32s3_hal::xtensa_lx::timer::set_ccompare0(0xffffffff);
94103
}

0 commit comments

Comments
 (0)