From bd5956166d20adbde3af0f6f265dc2f0ce5f4df9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 8 Apr 2026 13:53:46 +0200 Subject: hrtimer: Provide hrtimer_start_range_ns_user() Calvin reported an odd NMI watchdog lockup which claims that the CPU locked up in user space. He provided a reproducer, which set's up a timerfd based timer and then rearms it in a loop with an absolute expiry time of 1ns. As the expiry time is in the past, the timer ends up as the first expiring timer in the per CPU hrtimer base and the clockevent device is programmed with the minimum delta value. If the machine is fast enough, this ends up in a endless loop of programming the delta value to the minimum value defined by the clock event device, before the timer interrupt can fire, which starves the interrupt and consequently triggers the lockup detector because the hrtimer callback of the lockup mechanism is never invoked. The clockevents code already has a last resort mechanism to prevent that, but it's sensible to catch such issues before trying to reprogram the clock event device. Provide a variant of hrtimer_start_range_ns(), which sanity checks the timer after queueing it. It does not so before because the timer might be armed and therefore needs to be dequeued. also we optimize for the latest possible point to check, so that the clock event prevention is avoided as much as possible. If the timer is already expired _before_ the clock event is reprogrammed, remove the timer from the queue and signal to the caller that the operation failed by returning false. That allows the caller to take immediate action without going through the loops and hoops of the hrtimer interrupt. The queueing code can't invoke the timer callback as the caller might hold a lock which is taken in the callback. Add a tracepoint which allows to analyze the expired at start situation. Reported-by: Calvin Owens Signed-off-by: Thomas Gleixner Tested-by: Calvin Owens Reviewed-by: Frederic Weisbecker Acked-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260408114951.995031895@kernel.org --- include/linux/hrtimer.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 9ced498fefaa..66cc98c773cf 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -206,6 +206,9 @@ static inline void destroy_hrtimer_on_stack(struct hrtimer *timer) { } extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, u64 range_ns, const enum hrtimer_mode mode); +extern bool hrtimer_start_range_ns_user(struct hrtimer *timer, ktime_t tim, + u64 range_ns, const enum hrtimer_mode mode); + /** * hrtimer_start - (re)start an hrtimer * @timer: the timer to be added @@ -223,17 +226,28 @@ static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim, extern int hrtimer_cancel(struct hrtimer *timer); extern int hrtimer_try_to_cancel(struct hrtimer *timer); -static inline void hrtimer_start_expires(struct hrtimer *timer, - enum hrtimer_mode mode) +static inline void hrtimer_start_expires(struct hrtimer *timer, enum hrtimer_mode mode) { - u64 delta; ktime_t soft, hard; + u64 delta; + soft = hrtimer_get_softexpires(timer); hard = hrtimer_get_expires(timer); delta = ktime_to_ns(ktime_sub(hard, soft)); hrtimer_start_range_ns(timer, soft, delta, mode); } +static inline bool hrtimer_start_expires_user(struct hrtimer *timer, enum hrtimer_mode mode) +{ + ktime_t soft, hard; + u64 delta; + + soft = hrtimer_get_softexpires(timer); + hard = hrtimer_get_expires(timer); + delta = ktime_to_ns(ktime_sub(hard, soft)); + return hrtimer_start_range_ns_user(timer, soft, delta, mode); +} + void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl, enum hrtimer_mode mode); -- cgit v1.2.3 From 183d00b727139cf3b4be78d66a5602ce71a3acec Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 8 Apr 2026 13:54:11 +0200 Subject: alarmtimer: Provide alarm_start_timer() Alarm timers utilize hrtimers for normal operation and only switch to the RTC on suspend. In order to catch already expired timers early and without going through a timer interrupt cycle, provide a new start function which internally uses hrtimer_start_range_ns_user(). If hrtimer_start_range_ns_user() detects an already expired timer, it does not queue it. In that case remove the timer from the alarm base as well. Return the status queued or not back to the caller to handle the early expiry. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Acked-by: John Stultz Link: https://patch.msgid.link/20260408114952.332822525@kernel.org --- include/linux/alarmtimer.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 3ffa5341dce2..14d729fe0fd6 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -42,8 +42,14 @@ struct alarm { void *data; }; +static __always_inline ktime_t alarm_get_expires(struct alarm *alarm) +{ + return alarm->node.expires; +} + void alarm_init(struct alarm *alarm, enum alarmtimer_type type, void (*function)(struct alarm *, ktime_t)); +bool alarm_start_timer(struct alarm *alarm, ktime_t expires, bool relative); void alarm_start(struct alarm *alarm, ktime_t start); void alarm_start_relative(struct alarm *alarm, ktime_t start); void alarm_restart(struct alarm *alarm); -- cgit v1.2.3 From ed78a701941999635389c41ddd638e8e7ea2470f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 8 Apr 2026 13:54:33 +0200 Subject: alarmtimer: Remove unused interfaces All alarmtimer users are converted to alarm_start_timer(). Remove the now unused interfaces. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Link: https://patch.msgid.link/20260408114952.670899355@kernel.org --- include/linux/alarmtimer.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 14d729fe0fd6..2014288ca2f4 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -50,9 +50,6 @@ static __always_inline ktime_t alarm_get_expires(struct alarm *alarm) void alarm_init(struct alarm *alarm, enum alarmtimer_type type, void (*function)(struct alarm *, ktime_t)); bool alarm_start_timer(struct alarm *alarm, ktime_t expires, bool relative); -void alarm_start(struct alarm *alarm, ktime_t start); -void alarm_start_relative(struct alarm *alarm, ktime_t start); -void alarm_restart(struct alarm *alarm); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); -- cgit v1.2.3 From 33d4bfc49613301c8e451a597e377aaa331944bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:54:27 +0200 Subject: clocksource: Clean up clocksource_update_freq() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unused functions __clocksource_update_freq_hz() and __clocksource_update_freq_khz(). Then make __clocksource_update_freq_scale() static as it is not used from external callers anymore. Also clean up the comment accordingly. Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Acked-by: John Stultz Link: https://patch.msgid.link/20260504-clocksource-update_freq-v2-1-3e696fb01776@linutronix.de --- include/linux/clocksource.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 7c38190b10bf..c61aa458d4ec 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -236,8 +236,6 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec); */ extern int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq); -extern void -__clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq); /* * Don't call this unless you are a default clocksource @@ -258,16 +256,6 @@ static inline int clocksource_register_khz(struct clocksource *cs, u32 khz) return __clocksource_register_scale(cs, 1000, khz); } -static inline void __clocksource_update_freq_hz(struct clocksource *cs, u32 hz) -{ - __clocksource_update_freq_scale(cs, 1, hz); -} - -static inline void __clocksource_update_freq_khz(struct clocksource *cs, u32 khz) -{ - __clocksource_update_freq_scale(cs, 1000, khz); -} - #ifdef CONFIG_ARCH_CLOCKSOURCE_INIT extern void clocksource_arch_init(struct clocksource *cs); #else -- cgit v1.2.3 From 3af1f49f415dcac8c0df8bfc593df0371c219876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:56:10 +0200 Subject: hrtimer: Return ktime_t from hrtimer_get_next_event()/hrtimer_next_event_without() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions really work in terms of ktime_t and not u64. Change their return types and adapt the callers. Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260504-hrtimer-next_event-v2-1-7a5d0550b42f@linutronix.de --- include/linux/hrtimer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 66cc98c773cf..6862dea0acc5 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -268,8 +268,8 @@ static inline ktime_t hrtimer_get_remaining(const struct hrtimer *timer) return __hrtimer_get_remaining(timer, false); } -extern u64 hrtimer_get_next_event(void); -extern u64 hrtimer_next_event_without(const struct hrtimer *exclude); +extern ktime_t hrtimer_get_next_event(void); +extern ktime_t hrtimer_next_event_without(const struct hrtimer *exclude); extern bool hrtimer_active(const struct hrtimer *timer); -- cgit v1.2.3 From c8d32a0389fb97873285327ef4543a1431e54733 Mon Sep 17 00:00:00 2001 From: Gitle Mikkelsen Date: Fri, 1 May 2026 19:06:16 +0200 Subject: timers: Fix flseep() typo in kernel-doc comment Signed-off-by: Gitle Mikkelsen Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260501170616.1402-1-gitlem@gmail.com --- include/linux/delay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/delay.h b/include/linux/delay.h index 46412c00033a..68b2a69dd24d 100644 --- a/include/linux/delay.h +++ b/include/linux/delay.h @@ -110,7 +110,7 @@ static const unsigned int max_slack_shift = 2; * fsleep - flexible sleep which autoselects the best mechanism * @usecs: requested sleep duration in microseconds * - * flseep() selects the best mechanism that will provide maximum 25% slack + * fsleep() selects the best mechanism that will provide maximum 25% slack * to the requested sleep duration. Therefore it uses: * * * udelay() loop for sleep durations <= 10 microseconds to avoid hrtimer -- cgit v1.2.3