Skip to content

Commit 47372bb

Browse files
committed
esp32s3_tickless: fix 32-bit overflow in NSEC_2_CTICK/SEC_2_CTICK/USEC_2_CTICK
CONFIG_ESP32S3_TICKLESS hangs forever the first time a task calls a sleep/timeout with a fractional-second component of roughly 134ms or more (e.g. usleep(500000)) while another task is also pending a timeout. Root cause: NSEC_2_CTICK() computes ((nsec) * CTICK_PER_USEC) / NSEC_PER_USEC. `nsec` (struct timespec's tv_nsec) is a 32-bit `long`, and CTICK_PER_USEC is 16 (the S3's systimer runs at 16MHz), so the multiplication overflows a 32-bit signed int for any tv_nsec at or above INT32_MAX / 16 (~134,217,728 ns). The overflowed (negative) result then gets added into up_timer_start()'s `uint64_t cpu_ticks`, wrapping around to a value near UINT64_MAX. tickless_setcounter() then programs the systimer alarm that many ticks in the future -- effectively never -- so nxsched_process_timer() is never called and the waiting task sleeps forever. Reproduced on real esp32s3-xiao hardware: apps/testing/ostest hung indefinitely right after starting user_main(), whose first statement is usleep(500000). Instrumented up_timer_start() to print its inputs and observed exactly the described overflow (cpu_ticks close to UINT64_MAX for tv_nsec=510000000). Confirmed root cause is the concurrent-timeout case specifically: user_main's usleep() alone works, and ostest_main's own usleep() alone works, but the two together (matching ostest's actual task_create() + concurrent usleep() pattern) reproduce the hang every time. Fix: cast to uint64_t before multiplying in all three *_2_CTICK macros, forcing 64-bit arithmetic throughout, matching how the CTICK_2_* (division) macros are already overflow-safe. Validated on esp32s3-xiao: with the fix, the full ostest suite (built with CONFIG_ESP32S3_TICKLESS=y) runs past the point it used to hang and completes end to end. Note: while testing, ostest's own round-robin test (rr_test) failed near the end of the run -- the two same-priority SCHED_RR threads did not appear to interleave under tickless. That looks like a separate, likely more architectural issue (time-slice preemption needs its own periodic re-arm, independent of one-shot sleep timeouts) and is not addressed by this fix; filing separately. Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
1 parent 7609a72 commit 47372bb

1 file changed

Lines changed: 4 additions & 3 deletions

File tree

arch/xtensa/src/esp32s3/esp32s3_tickless.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@
7676
#define CTICK_PER_SEC (ESP32S3_SYSTIMER_TICKS_PER_SEC)
7777
#define CTICK_PER_USEC (CTICK_PER_SEC / USEC_PER_SEC)
7878

79-
#define SEC_2_CTICK(s) ((s) * CTICK_PER_SEC)
80-
#define USEC_2_CTICK(us) ((us) * CTICK_PER_USEC)
81-
#define NSEC_2_CTICK(nsec) (((nsec) * CTICK_PER_USEC) / NSEC_PER_USEC)
79+
#define SEC_2_CTICK(s) ((uint64_t)(s) * CTICK_PER_SEC)
80+
#define USEC_2_CTICK(us) ((uint64_t)(us) * CTICK_PER_USEC)
81+
#define NSEC_2_CTICK(nsec) (((uint64_t)(nsec) * CTICK_PER_USEC) / NSEC_PER_USEC)
8282

8383
#define CTICK_2_SEC(tick) ((tick) / CTICK_PER_SEC)
8484
#define CTICK_2_USEC(tick) ((tick) / CTICK_PER_USEC)
@@ -246,6 +246,7 @@ static int IRAM_ATTR tickless_isr(int irq, void *context, void *arg)
246246

247247
uint64_t unit_ticks = tickless_getcounter();
248248
uint64_t alarm_ticks = tickless_getalarmvalue();
249+
249250
if (unit_ticks < alarm_ticks)
250251
{
251252
modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET0_WORK_EN);

0 commit comments

Comments
 (0)