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
commit 580c599bd507ebca1f45d3eca1d451dfd99529b4 Author: ouyangxiangzhen <[email protected]> AuthorDate: Thu Apr 9 14:46:58 2026 +0800 sched/hrtimer: Fix overflow in hrtimer_reprogram Fix overflow issues in hrtimer_reprogram(): - Add HRTIMER_GUARD_DELAY = INT64_MAX - (1 << 48) ns. The headroom below INT64_MAX accommodates inter-core clock drift in SMP builds when each CPU independently updates the shared g_hrtimer_guard. - Set HRTIMER_MAX_DELAY to (INT64_MAX >> 1) + 1u so the maximum delay fits while leaving room for the guard. - Add hrtimer_last_expired() returning g_hrtimer_guard.expired minus HRTIMER_GUARD_DELAY, and compute the reprogram delay from it with HRTIMER_TIME_BEFORE_EQ, clamping to TICK2NSEC(g_oneshot_maxticks) under CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP. - Fix the ALARM_ARCH escape used in hrtimer_reprogram() to CONFIG_SCHED_TICKLESS_ALARM. Assisted-By: mimo-v2.6-pro Signed-off-by: ouyangxiangzhen <[email protected]> --- include/nuttx/hrtimer.h | 17 ++++++++++++++--- sched/hrtimer/hrtimer.h | 38 ++++++++++++++++++++++++++++++++++---- sched/hrtimer/hrtimer_gettime.c | 3 +-- sched/hrtimer/hrtimer_initialize.c | 2 +- sched/hrtimer/hrtimer_process.c | 4 ++++ 5 files changed, 54 insertions(+), 10 deletions(-) diff --git a/include/nuttx/hrtimer.h b/include/nuttx/hrtimer.h index 701a4ee8f54..94cd6f7595f 100644 --- a/include/nuttx/hrtimer.h +++ b/include/nuttx/hrtimer.h @@ -41,11 +41,22 @@ /* The maximum delay tick should be INT64_MAX. However, if there are expired * hrtimer in the queue, HRTIMER_TIME_BEFORE/AFTER might be incorrect, so we - * limited the delay to INT64_MAX >> 1, assuming all expired hrtimer can be - * processed within HRTIMER_MAX_DELAY. + * limited the delay to (INT64_MAX >> 1) + 1u, assuming all expired hrtimer + * can be processed within HRTIMER_MAX_DELAY - 1u. + * + * g_hrtimer_guard.expired is set to now + HRTIMER_GUARD_DELAY in the + * hrtimer_process. The guard delay is intentionally set to + * INT64_MAX - (1ull << 48) rather than INT64_MAX for the following reason: + * In SMP configurations, each CPU core independently updates shared + * g_hrtimer_guard.expired. Due to interrupt and lock latency, the current + * time across cores are not perfectly synchronized and may exhibit small + * forward/backward drift. + * The (1ull << 48) headroom (~0.3 days in nanoseconds) accommodates + * this inter-core time desynchronization. */ -#define HRTIMER_MAX_DELAY (INT64_MAX >> 1) +#define HRTIMER_GUARD_DELAY ((uint64_t)INT64_MAX - (1ull << 48)) +#define HRTIMER_MAX_DELAY (((uint64_t)INT64_MAX >> 1) + 1u) /**************************************************************************** * Public Types diff --git a/sched/hrtimer/hrtimer.h b/sched/hrtimer/hrtimer.h index 752f64465f5..52466a548d9 100644 --- a/sched/hrtimer/hrtimer.h +++ b/sched/hrtimer/hrtimer.h @@ -81,11 +81,12 @@ RB_HEAD(hrtimer_tree_s, hrtimer_s); extern seqcount_t g_hrtimer_lock; +extern struct hrtimer_s g_hrtimer_guard; + #ifdef CONFIG_HRTIMER_TREE /* Red-Black tree containing all active high-resolution timers */ extern struct hrtimer_tree_s g_hrtimer_tree; -extern struct hrtimer_s g_hrtimer_guard; extern struct FAR hrtimer_s *g_hrtimer_head; #else /* List containing all active high-resolution timers */ @@ -127,6 +128,25 @@ void hrtimer_process(uint64_t now); * Inline Functions ****************************************************************************/ +/**************************************************************************** + * Name: hrtimer_last_expired + * + * Description: + * Return the last expired time of the hrtimer. + * + * Input Parameters: + * None. + * + * Returned Value: + * The last expired time in nanoseconds. + * + ****************************************************************************/ + +static inline_function uint64_t hrtimer_last_expired(void) +{ + return g_hrtimer_guard.expired - HRTIMER_GUARD_DELAY; +} + /**************************************************************************** * Name: hrtimer_reprogram * @@ -154,12 +174,22 @@ static inline_function void hrtimer_reprogram(uint64_t next_expired) */ #ifdef CONFIG_SCHED_TICKLESS - int ret = 0; + int ret; struct timespec ts; + uint64_t delay; + uint64_t expired = hrtimer_last_expired(); + + delay = HRTIMER_TIME_BEFORE_EQ(expired, next_expired) ? + next_expired - expired : 0u; + +# ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP + delay = delay < TICK2NSEC((uint64_t)g_oneshot_maxticks) ? + delay : TICK2NSEC((uint64_t)g_oneshot_maxticks); +# endif - clock_nsec2time(&ts, next_expired); + clock_nsec2time(&ts, expired + delay); -# ifdef CONFIG_ALARM_ARCH +# ifdef CONFIG_SCHED_TICKLESS_ALARM ret = up_alarm_start(&ts); # else struct timespec current; diff --git a/sched/hrtimer/hrtimer_gettime.c b/sched/hrtimer/hrtimer_gettime.c index 9d2b5aa3a27..17376a58dfb 100644 --- a/sched/hrtimer/hrtimer_gettime.c +++ b/sched/hrtimer/hrtimer_gettime.c @@ -51,8 +51,7 @@ uint64_t hrtimer_gettime(FAR hrtimer_t *timer) { - uint64_t expire = hrtimer_read_64(&timer->expired); - int64_t remain = expire - clock_systime_nsec(); + int64_t remain = hrtimer_read_64(&timer->expired) - clock_systime_nsec(); return remain < 0 ? 0u : remain; } diff --git a/sched/hrtimer/hrtimer_initialize.c b/sched/hrtimer/hrtimer_initialize.c index 77f9af5bce2..f5cbba6c4a5 100644 --- a/sched/hrtimer/hrtimer_initialize.c +++ b/sched/hrtimer/hrtimer_initialize.c @@ -66,7 +66,7 @@ struct hrtimer_s g_hrtimer_guard = { &g_hrtimer_list, &g_hrtimer_list }, #endif NULL, - INT64_MAX + HRTIMER_GUARD_DELAY }; #ifdef CONFIG_HRTIMER_TREE diff --git a/sched/hrtimer/hrtimer_process.c b/sched/hrtimer/hrtimer_process.c index 8974964a57c..6d295be8ea2 100644 --- a/sched/hrtimer/hrtimer_process.c +++ b/sched/hrtimer/hrtimer_process.c @@ -134,6 +134,10 @@ void hrtimer_process(uint64_t now) hrtimer_unmark_running(cpu); + /* Note that the g_hrtimer_guard.expired is not monotonic. */ + + g_hrtimer_guard.expired = now + HRTIMER_GUARD_DELAY; + /* Schedule the next timer expiration */ if (expired != now)
