drivers/timer: Clamp after tick adjustment, not before
Some early tickless drivers had a common pattern where they would compute a tick maximum for the request (i.e. the maximum the hardware counter can handle) but apply it only on the input tick value and not on the adjusted final value, opening up the overflow condition it was supposed to have prevented. Fixes #20939 (Strictly it fixes the specific pattern that was discovered in that bug. It's not impossible that other drivers with alternative implementations have a similar issue, though they look OK to me via a quick audit). Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
parent
44bbdd0a94
commit
5f63c9d907
|
@ -39,7 +39,8 @@
|
|||
/*
|
||||
* Maximum number of ticks.
|
||||
*/
|
||||
#define MAX_TICKS (0x7FFFFFFFFFFFULL / RTC_COUNTS_PER_TICK)
|
||||
#define MAX_CYC 0x7FFFFFFFFFFFULL
|
||||
#define MAX_TICKS (MAX_CYC / RTC_COUNTS_PER_TICK)
|
||||
|
||||
/*
|
||||
* Due to the nature of clock synchronization, the comparator cannot be set
|
||||
|
@ -217,7 +218,7 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
|
|||
/* Round to the nearest tick boundary. */
|
||||
timeout = (timeout + RTC_COUNTS_PER_TICK - 1) / RTC_COUNTS_PER_TICK
|
||||
* RTC_COUNTS_PER_TICK;
|
||||
|
||||
timeout = MIN(timeout, MAX_CYC);
|
||||
timeout += rtc_last;
|
||||
|
||||
/* Set the comparator */
|
||||
|
|
|
@ -136,10 +136,17 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
|
|||
ticks = MAX(MIN(ticks - 1, (s32_t)max_ticks), 0);
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
u32_t now = MAIN_COUNTER_REG, cyc;
|
||||
u32_t now = MAIN_COUNTER_REG, cyc, adj;
|
||||
u32_t max_cyc = max_ticks * cyc_per_tick;
|
||||
|
||||
/* Round up to next tick boundary */
|
||||
cyc = ticks * cyc_per_tick + (now - last_count) + (cyc_per_tick - 1);
|
||||
/* Round up to next tick boundary. */
|
||||
cyc = ticks * cyc_per_tick;
|
||||
adj = (now - last_count) + (cyc_per_tick - 1);
|
||||
if (cyc <= max_cyc - adj) {
|
||||
cyc += adj;
|
||||
} else {
|
||||
cyc = max_cyc;
|
||||
}
|
||||
cyc = (cyc / cyc_per_tick) * cyc_per_tick;
|
||||
cyc += last_count;
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
#define CYC_PER_TICK ((u32_t)((u64_t)sys_clock_hw_cycles_per_sec() \
|
||||
/ (u64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
|
||||
#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK)
|
||||
#define MAX_CYC 0xffffffffu
|
||||
#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
|
||||
#define MIN_DELAY 1000
|
||||
|
||||
#define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && \
|
||||
|
@ -99,12 +100,15 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
|
|||
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
u64_t now = mtime();
|
||||
u32_t cyc = ticks * CYC_PER_TICK;
|
||||
u32_t adj, cyc = ticks * CYC_PER_TICK;
|
||||
|
||||
/* Round up to next tick boundary. Note use of 32 bit math,
|
||||
* max_ticks is calibrated to permit this.
|
||||
*/
|
||||
cyc += (u32_t)(now - last_count) + (CYC_PER_TICK - 1);
|
||||
/* Round up to next tick boundary. */
|
||||
adj = (u32_t)(now - last_count) + (CYC_PER_TICK - 1);
|
||||
if (cyc <= MAX_CYC - adj) {
|
||||
cyc += adj;
|
||||
} else {
|
||||
cyc = MAX_CYC;
|
||||
}
|
||||
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
|
||||
|
||||
if ((s32_t)(cyc + last_count - now) < MIN_DELAY) {
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \
|
||||
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC)
|
||||
#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK)
|
||||
#define MAX_CYC 0xffffffffu
|
||||
#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
|
||||
#define MIN_DELAY 1000
|
||||
|
||||
static struct k_spinlock lock;
|
||||
|
@ -74,10 +75,16 @@ void z_clock_set_timeout(s32_t ticks, bool idle)
|
|||
ticks = MAX(MIN(ticks - 1, (s32_t)MAX_TICKS), 0);
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
u32_t curr = ccount(), cyc;
|
||||
u32_t curr = ccount(), cyc, adj;
|
||||
|
||||
/* Round up to next tick boundary */
|
||||
cyc = ticks * CYC_PER_TICK + (curr - last_count) + (CYC_PER_TICK - 1);
|
||||
cyc = ticks * CYC_PER_TICK;
|
||||
adj = (curr - last_count) + (CYC_PER_TICK - 1);
|
||||
if (cyc <= MAX_CYC - adj) {
|
||||
cyc += adj;
|
||||
} else {
|
||||
cyc = MAX_CYC;
|
||||
}
|
||||
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
|
||||
cyc += last_count;
|
||||
|
||||
|
|
Loading…
Reference in a new issue