This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 3026e322d80 esp32s3_tickless: fix 32-bit overflow in 
NSEC_2_CTICK/SEC_2_CTICK/USEC_2_CTICK
3026e322d80 is described below

commit 3026e322d80ea3c0ade464d22e3d5c7afcbe8f8c
Author: Felipe Moura <[email protected]>
AuthorDate: Mon Aug 31 12:01:03 2026 -0300

    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 <[email protected]>
---
 arch/xtensa/src/esp32s3/esp32s3_tickless.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/arch/xtensa/src/esp32s3/esp32s3_tickless.c 
b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
index 3a3a1d7f5e7..d830ce4017f 100644
--- a/arch/xtensa/src/esp32s3/esp32s3_tickless.c
+++ b/arch/xtensa/src/esp32s3/esp32s3_tickless.c
@@ -76,9 +76,9 @@
 #define CTICK_PER_SEC         (ESP32S3_SYSTIMER_TICKS_PER_SEC)
 #define CTICK_PER_USEC        (CTICK_PER_SEC / USEC_PER_SEC)
 
-#define SEC_2_CTICK(s)        ((s) * CTICK_PER_SEC)
-#define USEC_2_CTICK(us)      ((us) * CTICK_PER_USEC)
-#define NSEC_2_CTICK(nsec)    (((nsec) * CTICK_PER_USEC) / NSEC_PER_USEC)
+#define SEC_2_CTICK(s)        ((uint64_t)(s) * CTICK_PER_SEC)
+#define USEC_2_CTICK(us)      ((uint64_t)(us) * CTICK_PER_USEC)
+#define NSEC_2_CTICK(nsec)    (((uint64_t)(nsec) * CTICK_PER_USEC) / 
NSEC_PER_USEC)
 
 #define CTICK_2_SEC(tick)     ((tick) / CTICK_PER_SEC)
 #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)
 
   uint64_t unit_ticks = tickless_getcounter();
   uint64_t alarm_ticks = tickless_getalarmvalue();
+
   if (unit_ticks < alarm_ticks)
     {
       modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET0_WORK_EN);

Reply via email to