Re: The "clockevents: Prevent timer interrupt starvation" patch causes lockups

From: Frederic Weisbecker

Date: Tue Apr 14 2026 - 14:05:31 EST


Le Tue, Apr 14, 2026 at 03:39:00PM +0000, Eric Naim a écrit :
> On 4/14/26 5:20 AM, Hanabishi wrote:
> >
> > Hello.
> >
> > Sorry, but this patch as of 7.0 introduced *severe* periodic lockups on my
> > Ryzen 7700X machine.
> > I see such messages in the log:
> >
> > clocksource: Long readout interval, skipping watchdog check: cs_nsec:
> > 2897344852 wd_nsec: 2897356996
> >
> > Reverting d6e152d905bdb1f32f9d99775e2f453350399a6a for mainline fixes the
> > issue for me.
> >
>
> Hi maintainers,
>
> several users from CachyOS has reported this regression as well. We landed on
> the same bisection. One of the users that could reproduce this reliably
> reproduced this just by watching a YouTube video in a browser, and observed
> freezes and stutters when interacting with the system.
>
> I had an LLM generate a fix (patch attached), and it fixed the regression for
> that user. Full disclosure: it is written completely by AI, and I am also not
> familiar with this subsystem. I just hope that this patch can be helpful in
> fixing the regression.
>
> Please don't hesitate to tell me off if utilizing AI in this way is not
> helpful, so I can keep this in mind for future contributions.
>
>
> --
> Regards,
> Eric

> diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
> index 38570998a19b..37b10045572e 100644
> --- a/kernel/time/clockevents.c
> +++ b/kernel/time/clockevents.c
> @@ -332,8 +332,10 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
> if (delta > (int64_t)dev->min_delta_ns) {
> delta = min(delta, (int64_t) dev->max_delta_ns);
> clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
> - if (!dev->set_next_event((unsigned long) clc, dev))
> + if (!dev->set_next_event((unsigned long) clc, dev)) {
> + dev->next_event_forced = 0;
> return 0;
> + }
> }
>
> if (dev->next_event_forced)
> diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c
> index 7472597f3225..bf411472d4f7 100644
> --- a/kernel/time/tick-oneshot.c
> +++ b/kernel/time/tick-oneshot.c
> @@ -34,6 +34,7 @@ int tick_program_event(ktime_t expires, int force)
> */
> clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
> dev->next_event = KTIME_MAX;
> + dev->next_event_forced = 0;
> return 0;
> }
>
> @@ -43,6 +44,7 @@ int tick_program_event(ktime_t expires, int force)
> * before using it.
> */
> clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
> + dev->next_event_forced = 0;
> }
>
> return clockevents_program_event(dev, expires, force);

That diff suggest that dev->next_event_forced is not properly cleared by
a handler or when the device is stopped.

For example it's not cleared when the device is oneshot stopped.

It's also not cleared when the device is detached (though that shouldn't
matter much) and also when the broadcast wakeup thing is used.

Can you try the following?

diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index b4d730604972..5c6dfd6bed28 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -100,6 +100,7 @@ static int __clockevents_switch_state(struct clock_event_device *dev,
/* The clockevent device is getting replaced. Shut it down. */

case CLOCK_EVT_STATE_SHUTDOWN:
+ dev->next_event_forced = 0;
if (dev->set_state_shutdown)
return dev->set_state_shutdown(dev);
return 0;
@@ -127,10 +128,12 @@ static int __clockevents_switch_state(struct clock_event_device *dev,
clockevent_get_state(dev)))
return -EINVAL;

- if (dev->set_state_oneshot_stopped)
+ if (dev->set_state_oneshot_stopped) {
+ dev->next_event_forced = 0;
return dev->set_state_oneshot_stopped(dev);
- else
+ } else {
return -ENOSYS;
+ }

default:
return -ENOSYS;
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 7e57fa31ee26..115e0bf01276 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -108,6 +108,7 @@ static struct clock_event_device *tick_get_oneshot_wakeup_device(int cpu)

static void tick_oneshot_wakeup_handler(struct clock_event_device *wd)
{
+ wd->next_event_forced = 0;
/*
* If we woke up early and the tick was reprogrammed in the
* meantime then this may be spurious but harmless.