From nobody Mon Jun 15 06:29:50 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C8523D3323; Wed, 8 Apr 2026 15:09:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775660979; cv=none; b=W89wRiO3HIJSqrdKE+vbHNEJBQorvL7KB2RfPj3vTf1RS2iisQPCiEjIeB2u9cRG3hYPfXLx1siLhjwN3BV299B8jZte7djcx1TMjjnIcEU2WFwWLmqRFflQ2PzwEWZbyFxmYHlUjFX+wjOGD8RbszYAzf8fSX6cJ0N0th43Diw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775660979; c=relaxed/simple; bh=obxNqbuZi7wqR3x2VZR7tfjFnJNGwemqBGXQlxUym2E=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=Doz2tBzouCWnxSKvNKfgAS/fMeJywPQ2X8AU+zQQnGCtW7y0Jx+u3x6B9fMzrU4CKSIYz0mgSV1nfva3TBK4GlBVnW2l+lfH5d5VEg1o37sgrKiPreiQvqjSgB0iCBZFEnbTR+MRJtgmUEdMdre2OD6tf0s3QAzS++aHNfYMnRI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=09Zn+6w3; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=bl1ocXJ9; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="09Zn+6w3"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="bl1ocXJ9" Date: Wed, 08 Apr 2026 15:09:34 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1775660976; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bfInr1SWJuTlwnBlubN5+/bxUHuLcVTt9R1QtHFzxJs=; b=09Zn+6w3YmZoZsf+EHHwh3uvEVzyTad0HNIVfBZitM0DV2WM4nH7mM8yVugkMf0R0iauAH 0w1tSDXSDInIyPZAr49wd6JNCvgzTi7ek2a9OcZjUlt08reeJ6WlyBhx91h3z4oAWhQB8J KRopB2x6XiPac6b9Dwi4yWcLi+k5WSVGxFV685uznAn/T/PRwM8zhiZsG61Gi13bj4RIFn DnQl0QSu6onySR5Olt9+854jmOwtXUwZVp5NAvxVjKJSarIVs2BzzDwpHeWPPq4UZ4tnd4 Jqxl+d4+fYNUZTKblSnU9bAlLE5fqKcsFrJRYFw7sCH6OTb6E5SzsQ0NvGJ7vg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1775660976; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bfInr1SWJuTlwnBlubN5+/bxUHuLcVTt9R1QtHFzxJs=; b=bl1ocXJ98RGuF27SA9BvPxXyffBd4kfNRv/92zEhk3GV/Ku4ThKdbAf/8r2qTwipLH5PiI xtIFyRAdxPq0C9DA== From: "tip-bot2 for Thomas Gleixner" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: timers/urgent] clockevents: Prevent timer interrupt starvation Cc: Calvin Owens , Thomas Gleixner , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <20260407083247.562657657@kernel.org> References: <20260407083247.562657657@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <177566097442.801717.12615913033639115651.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the timers/urgent branch of tip: Commit-ID: 1c2eabb8805d9fd79a19de5c76d4a64c9ad3cdf4 Gitweb: https://git.kernel.org/tip/1c2eabb8805d9fd79a19de5c76d4a64c9= ad3cdf4 Author: Thomas Gleixner AuthorDate: Tue, 07 Apr 2026 10:54:17 +02:00 Committer: Thomas Gleixner CommitterDate: Wed, 08 Apr 2026 17:06:11 +02:00 clockevents: Prevent timer interrupt starvation Calvin reported an odd NMI watchdog lockup which claims that the CPU locked up in user space. He provided a reproducer, which sets 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. As a first step to prevent this, avoid reprogramming the clock event device when: - a forced minimum delta event is pending - the new expiry delta is less then or equal to the minimum delta Thanks to Calvin for providing the reproducer and to Borislav for testing and providing data from his Zen5 machine. The problem is not limited to Zen5, but depending on the underlying clock event device (e.g. TSC deadline timer on Intel) and the CPU speed not necessarily observable. This change serves only as the last resort and further changes will be made to prevent this scenario earlier in the call chain as far as possible. [ tglx: Updated to restore the old behaviour vs. !force and delta <=3D 0 ] Fixes: d316c57ff6bf ("[PATCH] clockevents: add core functionality") Reported-by: Calvin Owens Signed-off-by: Thomas Gleixner Tested-by: Calvin Owens Link: https://lore.kernel.org/lkml/acMe-QZUel-bBYUh@mozart.vkv.me/ Link: https://patch.msgid.link/20260407083247.562657657@kernel.org --- include/linux/clockchips.h | 2 ++ kernel/time/clockevents.c | 27 +++++++++++++++++++-------- kernel/time/hrtimer.c | 1 + kernel/time/tick-common.c | 1 + kernel/time/tick-sched.c | 1 + 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index b0df28d..50cdc9d 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -80,6 +80,7 @@ enum clock_event_state { * @shift: nanoseconds to cycles divisor (power of two) * @state_use_accessors:current state of the device, assigned by the core = code * @features: features + * @next_event_forced: True if the last programming was a forced event * @retries: number of forced programming retries * @set_state_periodic: switch state to periodic * @set_state_oneshot: switch state to oneshot @@ -108,6 +109,7 @@ struct clock_event_device { u32 shift; enum clock_event_state state_use_accessors; unsigned int features; + unsigned int next_event_forced; unsigned long retries; =20 int (*set_state_periodic)(struct clock_event_device *); diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index eaae1ce..3857099 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -172,6 +172,7 @@ void clockevents_shutdown(struct clock_event_device *de= v) { clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN); dev->next_event =3D KTIME_MAX; + dev->next_event_forced =3D 0; } =20 /** @@ -305,7 +306,6 @@ int clockevents_program_event(struct clock_event_device= *dev, ktime_t expires, { unsigned long long clc; int64_t delta; - int rc; =20 if (WARN_ON_ONCE(expires < 0)) return -ETIME; @@ -324,16 +324,27 @@ int clockevents_program_event(struct clock_event_devi= ce *dev, ktime_t expires, return dev->set_next_ktime(expires, dev); =20 delta =3D ktime_to_ns(ktime_sub(expires, ktime_get())); - if (delta <=3D 0) - return force ? clockevents_program_min_delta(dev) : -ETIME; =20 - delta =3D min(delta, (int64_t) dev->max_delta_ns); - delta =3D max(delta, (int64_t) dev->min_delta_ns); + /* Required for tick_periodic() during early boot */ + if (delta <=3D 0 && !force) + return -ETIME; + + if (delta > (int64_t)dev->min_delta_ns) { + delta =3D min(delta, (int64_t) dev->max_delta_ns); + clc =3D ((unsigned long long) delta * dev->mult) >> dev->shift; + if (!dev->set_next_event((unsigned long) clc, dev)) + return 0; + } =20 - clc =3D ((unsigned long long) delta * dev->mult) >> dev->shift; - rc =3D dev->set_next_event((unsigned long) clc, dev); + if (dev->next_event_forced) + return 0; =20 - return (rc && force) ? clockevents_program_min_delta(dev) : rc; + if (dev->set_next_event(dev->min_delta_ticks, dev)) { + if (!force || clockevents_program_min_delta(dev)) + return -ETIME; + } + dev->next_event_forced =3D 1; + return 0; } =20 /* diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 860af7a..1e37142 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1888,6 +1888,7 @@ void hrtimer_interrupt(struct clock_event_device *dev) BUG_ON(!cpu_base->hres_active); cpu_base->nr_events++; dev->next_event =3D KTIME_MAX; + dev->next_event_forced =3D 0; =20 raw_spin_lock_irqsave(&cpu_base->lock, flags); entry_time =3D now =3D hrtimer_update_base(cpu_base); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index d305d85..6a9198a 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -110,6 +110,7 @@ void tick_handle_periodic(struct clock_event_device *de= v) int cpu =3D smp_processor_id(); ktime_t next =3D dev->next_event; =20 + dev->next_event_forced =3D 0; tick_periodic(cpu); =20 /* diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 36449f0..d1f27df 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -1513,6 +1513,7 @@ static void tick_nohz_lowres_handler(struct clock_eve= nt_device *dev) struct tick_sched *ts =3D this_cpu_ptr(&tick_cpu_sched); =20 dev->next_event =3D KTIME_MAX; + dev->next_event_forced =3D 0; =20 if (likely(tick_nohz_handler(&ts->sched_timer) =3D=3D HRTIMER_RESTART)) tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);