Skip to content

Commit 236d910

Browse files
committed
Merge branch 'bugfix/light_sleep_deadlock' into 'master'
esp_hw_support: Fix light sleep deadlock Closes IDFCI-1329 and IDFCI-1330 See merge request espressif/esp-idf!19278
2 parents cacd27f + a73dd07 commit 236d910

File tree

5 files changed

+48
-5
lines changed

5 files changed

+48
-5
lines changed

components/esp_hw_support/esp_clk.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <sys/param.h>
99
#include <sys/lock.h>
1010

11+
#include "freertos/FreeRTOS.h"
1112
#include "esp_attr.h"
1213
#include "soc/rtc.h"
1314
#include "soc/soc_caps.h"
@@ -46,7 +47,7 @@ extern uint32_t g_ticks_per_us_app;
4647
#endif
4748
#endif
4849

49-
static _lock_t s_esp_rtc_time_lock;
50+
static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED;
5051

5152
// TODO: IDF-4239
5253
static RTC_DATA_ATTR uint64_t s_esp_rtc_time_us = 0, s_rtc_last_ticks = 0;
@@ -94,7 +95,7 @@ uint64_t esp_rtc_get_time_us(void)
9495
//IDF-3901
9596
return 0;
9697
#endif
97-
_lock_acquire(&s_esp_rtc_time_lock);
98+
portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
9899
const uint32_t cal = esp_clk_slowclk_cal_get();
99100
const uint64_t rtc_this_ticks = rtc_time_get();
100101
const uint64_t ticks = rtc_this_ticks - s_rtc_last_ticks;
@@ -115,7 +116,7 @@ uint64_t esp_rtc_get_time_us(void)
115116
((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT));
116117
s_esp_rtc_time_us += delta_time_us;
117118
s_rtc_last_ticks = rtc_this_ticks;
118-
_lock_release(&s_esp_rtc_time_lock);
119+
portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
119120
return s_esp_rtc_time_us;
120121
}
121122

@@ -143,3 +144,13 @@ uint64_t esp_clk_rtc_time(void)
143144
return 0;
144145
#endif
145146
}
147+
148+
void esp_clk_private_lock(void)
149+
{
150+
portENTER_CRITICAL(&s_esp_rtc_time_lock);
151+
}
152+
153+
void esp_clk_private_unlock(void)
154+
{
155+
portEXIT_CRITICAL(&s_esp_rtc_time_lock);
156+
}

components/esp_hw_support/include/esp_private/esp_clk.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ int esp_clk_xtal_freq(void);
8282
*/
8383
uint64_t esp_clk_rtc_time(void);
8484

85+
/**
86+
* @brief obtain internal critical section used esp_clk implementation.
87+
*
88+
* This is used by the esp_light_sleep_start() to avoid deadlocking when it
89+
* calls esp_clk related API after stalling the other CPU.
90+
*/
91+
void esp_clk_private_lock(void);
92+
93+
/**
94+
* @brief counterpart of esp_clk_private_lock
95+
*/
96+
void esp_clk_private_unlock(void);
97+
8598
#ifdef __cplusplus
8699
}
87100
#endif

components/esp_hw_support/sleep_modes.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,12 +665,29 @@ esp_err_t esp_light_sleep_start(void)
665665
s_config.ccount_ticks_record = esp_cpu_get_cycle_count();
666666
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
667667
portENTER_CRITICAL(&light_sleep_lock);
668+
/*
669+
Note: We are about to stall the other CPU via the esp_ipc_isr_stall_other_cpu(). However, there is a chance of
670+
deadlock if after stalling the other CPU, we attempt to take spinlocks already held by the other CPU that is.
671+
672+
Thus any functions that we call after stalling the other CPU will need to have the locks taken first to avoid
673+
deadlock.
674+
675+
Todo: IDF-5257
676+
*/
677+
668678
/* We will be calling esp_timer_private_set inside DPORT access critical
669679
* section. Make sure the code on the other CPU is not holding esp_timer
670680
* lock, otherwise there will be deadlock.
671681
*/
672682
esp_timer_private_lock();
673683

684+
/* We will be calling esp_rtc_get_time_us() below. Make sure the code on the other CPU is not holding the
685+
* esp_rtc_get_time_us() lock, otherwise there will be deadlock. esp_rtc_get_time_us() is called via:
686+
*
687+
* - esp_clk_slowclk_cal_set() -> esp_rtc_get_time_us()
688+
*/
689+
esp_clk_private_lock();
690+
674691
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
675692
uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count();
676693
uint64_t high_res_time_at_start = esp_timer_get_time();
@@ -793,6 +810,7 @@ esp_err_t esp_light_sleep_start(void)
793810
}
794811
esp_set_time_from_rtc();
795812

813+
esp_clk_private_unlock();
796814
esp_timer_private_unlock();
797815
esp_ipc_isr_release_other_cpu();
798816
if (!wdt_was_enabled) {

components/esp_system/include/esp_ipc_isr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ void esp_ipc_isr_asm_call_blocking(esp_ipc_isr_func_t func, void* arg);
6262
* - If the stall feature is paused using esp_ipc_isr_stall_pause(), this function will have no effect
6363
*
6464
* @note This function is not available in single-core mode.
65+
* @note It is the caller's responsibility to avoid deadlocking on spinlocks
6566
*/
6667
void esp_ipc_isr_stall_other_cpu(void);
6768

components/esp_timer/include/esp_private/esp_timer_private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ void esp_timer_private_set(uint64_t new_us);
5151
void esp_timer_private_advance(int64_t time_diff_us);
5252

5353
/**
54-
* @brief obtain internal critical section used esp_timer implementation
54+
* @brief obtain internal critical section used in the esp_timer implementation
5555
* This can be used when a sequence of calls to esp_timer has to be made,
5656
* and it is necessary that the state of the timer is consistent between
5757
* the calls. Should be treated in the same way as a spinlock.
58-
* Call esp_timer_unlock to release the lock
58+
* Call esp_timer_private_unlock to release the lock
5959
*/
6060
void esp_timer_private_lock(void);
6161

0 commit comments

Comments
 (0)