FelipeMdeO opened a new pull request, #20179:
URL: https://github.com/apache/nuttx/pull/20179
## Summary
`up_idlepm()` (`arch/xtensa/src/esp32s3/esp32s3_idle.c`,
`arch/xtensa/src/esp32/esp32_idle.c`,
`arch/xtensa/src/esp32s2/esp32s2_idle.c`
and the shared RISC-V `arch/risc-v/src/common/espressif/esp_idle.c`, used by
esp32c3/esp32c6) has a recovery branch that forces the domain back to
PM_NORMAL when `oldstate` is not PM_NORMAL and nothing is currently staying
at it:
```c
pm_stay(PM_IDLE_DOMAIN, PM_NORMAL);
pm_changestate(PM_IDLE_DOMAIN, PM_NORMAL);
newstate = PM_NORMAL;
```
`pm_stay()` here has no matching `pm_relax()` anywhere in any of the four
files. The first time this branch runs, the stay count for PM_NORMAL never
returns to 0, and `pm_checkstate()` (called unconditionally right after
this block) can never recommend anything deeper than PM_NORMAL again for
the rest of uptime -- the idle loop keeps running, but the governor is
permanently pinned at full power, with no further light or deep sleep.
The trigger is timing-dependent (whether anything else already holds
PM_NORMAL at the moment this branch runs), which is likely why it does not
reproduce on every single boot.
Fix: release the stay right after the one `pm_changestate()` call it exists
to force, matching the comment already there ("Keep working in normal
stage") -- a one-shot nudge, not a standing hold.
## Impact
* Is new feature added? No -- pure bug fix.
* Is existing feature changed? No behavior change for configs that never
hit the leaking branch; for configs that do, this restores the intended
ability to reach light/deep sleep after a PM_NORMAL recovery event.
* Impact on hardware? Affects every board using `CONFIG_PM` +
`CONFIG_SCHED_TICKLESS` on esp32, esp32s2, esp32s3, esp32c3 or esp32c6 --
generic arch-level code, no board files touched.
## Testing
I confirm that changes are verified on local setup and works as intended:
* Build Host: Ubuntu 24.04, x86_64, `xtensa-esp-elf-gcc` (crosstool-NG
esp-14.2.0_20241119, 14.2.0).
* Target: Xtensa, Seeed XIAO ESP32-S3, out-of-tree defconfig with
`CONFIG_PM=y`, `CONFIG_SCHED_TICKLESS=y`, `CONFIG_ESPRESSIF_WIFI=y`.
Confirmed via JTAG (OpenOCD + GDB), reading `g_pmdomains[0]` live in memory:
**Before the fix** -- a "system" wakelock stuck at `state=PM_NORMAL,
count=1`, acquired a few seconds after boot (right when Wi-Fi coming up
briefly moves the domain off PM_NORMAL and this branch forces it back).
Over a 40+ minute run: `state = 0` (PM_NORMAL) the entire time, zero
`newstate=2` (PM_STANDBY) transitions logged, zero light-sleep-return log
lines.
```
$1 = {state = 0 '\000', ...,
wakelock = {{head = 0x3fca1684 <g_wakelock+44>, tail = 0x3fca1684
<g_wakelock+44>}, ...},
...}
$2 = {name = "system", '\000' <repeats 25 times>, domain = 0,
state = PM_NORMAL, count = 1, ...,
start = {tv_sec = 12, tv_nsec = 119949125}, ...}
```
Console log before the fix (only two PM transitions ever logged, the
second one being the leak):
```
up_idlepm: newstate= 1 oldstate=0
up_idlepm: newstate= 0 oldstate=1
```
(nothing else for the following 40 minutes)
**After the fix**, same board, same config, fresh boot -- reached
PM_STANDBY (real light sleep) within seconds:
```
up_idlepm: newstate= 1 oldstate=0
up_idlepm: newstate= 2 oldstate=1
```
And reading `g_pmdomains[0]` live again, caught mid-sleep:
```
$1 = {state = 2 '\002', in_sleep = true,
wakelock = {{head = 0x0, tail = 0x0}, /* PM_NORMAL: empty
now */
{head = 0x0, tail = 0x0}, /* PM_IDLE: empty */
{head = 0x3fca1774 <g_wakelock+284>, ...}, /* PM_STANDBY:
the board's own intentional floor blocking deep sleep, unrelated to this bug */
{head = 0x0, tail = 0x0}},
...}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]