Skip to content

Commit 9075d53

Browse files
mjchen0kartben
authored andcommitted
kernel: fix timeout bugs
When CONFIG_TIMEOUT_64BIT is y, positive values are relative/delta timeouts and negative values are absolute timeouts, except for two special values. -1 is K_WAIT_FOREVER and 0 is K_NO_WAIT. The reserved value of -1 means INT64_MAX is not a valid argument to K_TIMEOUT_ABS_TICKS(), but there was no check. If a literal was passed, a preprocessor/compiler warning would be generated for overflow, but if a variable was passed as the argument, then the code would compile but not work correctly since the absolute timeout would be changed to a relative one. One example of this is task_wdt_init() if no channels are enabled. Rather than just fixing task_wdt, and trying to find other cases in an adhoc way, this CL changes K_TIMEOUT_ABS_TICKS() to limit the larges value to (INT64_MAX-1). It does so silently, but given the range of int64_t, there should be no practical difference. Also, change the implementation for Z_IS_TIMEOUT_RELATIVE() to fix the case where INT64_MAX relative timeout was being improperly reported as being not a relative timeout. This was again due to the -1 reserved value. Add some tests for these changes to the timer_api test. Signed-off-by: Mike J. Chen <mjchen@google.com>
1 parent 6d88a62 commit 9075d53

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

include/zephyr/kernel.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,13 +1488,13 @@ const char *k_thread_state_str(k_tid_t thread_id, char *buf, size_t buf_size);
14881488
* This macro generates a timeout delay that represents an expiration
14891489
* at the absolute uptime value specified, in system ticks. That is, the
14901490
* timeout will expire immediately after the system uptime reaches the
1491-
* specified tick count.
1491+
* specified tick count. Value is clamped to the range 0 to INT64_MAX-1.
14921492
*
14931493
* @param t Tick uptime value
14941494
* @return Timeout delay value
14951495
*/
14961496
#define K_TIMEOUT_ABS_TICKS(t) \
1497-
Z_TIMEOUT_TICKS(Z_TICK_ABS((k_ticks_t)MAX(t, 0)))
1497+
Z_TIMEOUT_TICKS(Z_TICK_ABS((k_ticks_t)CLAMP(t, 0, (INT64_MAX - 1))))
14981498

14991499
/**
15001500
* @brief Generates an absolute/uptime timeout value from seconds

include/zephyr/sys_clock.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,14 @@ typedef struct {
151151

152152
/* Test for relative timeout */
153153
#if CONFIG_TIMEOUT_64BIT
154-
#define Z_IS_TIMEOUT_RELATIVE(timeout) (Z_TICK_ABS((timeout).ticks) < 0)
154+
/* Positive values are relative/delta timeouts and negative values are absolute
155+
* timeouts, except -1 which is reserved for K_TIMEOUT_FOREVER. 0 is K_NO_WAIT,
156+
* which is historically considered a relative timeout.
157+
* K_TIMEOUT_FOREVER is not considered a relative timeout and neither is it
158+
* considerd an absolute timeouts (so !Z_IS_TIMEOUT_RELATIVE() does not
159+
* necessarily mean it is an absolute timeout if ticks == -1);
160+
*/
161+
#define Z_IS_TIMEOUT_RELATIVE(timeout) (((timeout).ticks) >= 0)
155162
#else
156163
#define Z_IS_TIMEOUT_RELATIVE(timeout) true
157164
#endif

tests/kernel/timer/timer_api/src/main.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,33 @@ ZTEST_USER(timer_api, test_timeout_abs)
752752
/* Ensure second alignment for K_TIMEOUT_ABS_SEC */
753753
zassert_true(exp_ms % MSEC_PER_SEC == 0);
754754

755+
/* Check K_TIMEOUT_ABS_TICKS() and Z_IS_TIMEOUT_RELATIVE macros */
756+
t2 = K_NO_WAIT;
757+
zassert_true(Z_IS_TIMEOUT_RELATIVE(t2));
758+
t2 = K_TICKS(1);
759+
zassert_true(Z_IS_TIMEOUT_RELATIVE(t2));
760+
t2 = K_TICKS(INT64_MAX-1);
761+
zassert_true(Z_IS_TIMEOUT_RELATIVE(t2));
762+
t2 = K_TICKS(INT64_MAX);
763+
zassert_true(Z_IS_TIMEOUT_RELATIVE(t2));
764+
765+
zassert_false(Z_IS_TIMEOUT_RELATIVE(t));
766+
t2 = K_TIMEOUT_ABS_TICKS(1);
767+
zassert_false(Z_IS_TIMEOUT_RELATIVE(t2));
768+
t2 = K_TIMEOUT_ABS_TICKS(INT64_MAX-1);
769+
zassert_false(Z_IS_TIMEOUT_RELATIVE(t2));
770+
771+
/* Check when INT64_MAX passed to K_TIMEOUT_ABS_TICKS(), with
772+
* both a literal and variable argument.
773+
*/
774+
t2 = K_TIMEOUT_ABS_TICKS(INT64_MAX);
775+
zassert_false(Z_IS_TIMEOUT_RELATIVE(t2));
776+
777+
uint64_t max_int64 = INT64_MAX;
778+
779+
t2 = K_TIMEOUT_ABS_TICKS(max_int64);
780+
zassert_false(Z_IS_TIMEOUT_RELATIVE(t2));
781+
755782
/* Check the other generator macros to make sure they produce
756783
* the same (whiteboxed) converted values
757784
*/
@@ -796,6 +823,33 @@ ZTEST_USER(timer_api, test_timeout_abs)
796823
t0+rem_ticks-exp_ticks);
797824

798825
k_timer_stop(&remain_timer);
826+
827+
/* Rerun test with t set to INT64_MAX. K_TIMEOUT_ABS_TICKS()
828+
* should adjust the abs timeout to INT64_MAX-1 because the negative
829+
* range used for absolute timeouts needs to reserve -1 for
830+
* K_TIMEOUT_FOREVER.
831+
*/
832+
init_timer_data();
833+
exp_ticks = INT64_MAX - 1;
834+
k_timer_start(&remain_timer, K_TIMEOUT_ABS_TICKS(INT64_MAX), K_FOREVER);
835+
836+
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
837+
k_usleep(1);
838+
}
839+
840+
do {
841+
t0 = k_uptime_ticks();
842+
rem_ticks = k_timer_remaining_ticks(&remain_timer);
843+
t1 = k_uptime_ticks();
844+
} while (t0 != t1);
845+
846+
zassert_true(t0 + rem_ticks == exp_ticks,
847+
"Wrong remaining: now %lld rem %lld expires %lld (%lld)",
848+
(uint64_t)t0, (uint64_t)rem_ticks, (uint64_t)exp_ticks,
849+
t0+rem_ticks-exp_ticks);
850+
851+
k_timer_stop(&remain_timer);
852+
799853
#endif
800854
}
801855

0 commit comments

Comments
 (0)