From nobody Sat Jun 20 17:37:24 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 8C2E5313E10 for ; Sun, 12 Apr 2026 13:42:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776001348; cv=none; b=PsCsRL93Sy1vFM9onk++/pOuxxJFsjLQ+cdQ5TQkdyy/UemGEwaojD4NVzuY5Bb0DQd3DB0aVfHBcRMvT7V1oIMs4M1YbxfQAupF39eL7FLt8Dswk/8Tb4vRJ9QQScdGO3T3u/6tg0lXbnLQQsDpatrl1EaduHIFPRC9Dl7Pa2Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776001348; c=relaxed/simple; bh=fqKVsg5WaB6X3s8lPGP6KsCGTJsRoqueJFV5SmfwOcg=; h=Date:From:To:Cc:Subject:Message-ID:Content-Type:MIME-Version; b=PxCk7r9pzQa8pe7lysVsLcwpHcKZ783g/1p/0tjw8U1CuIPGYhtdmHnnY55Uju1N0mdq6UJT55nCLYZY7TZqKJTXTU+XXdxyLtP6eVriW9mapfgRCu1aa3BEMxeFbWeBT8UkgrPUG4Sdw2mXT7hKJSdMyN76IKHHpU8T7i6lT5o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Awng/CEV; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Awng/CEV" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C8129C19424; Sun, 12 Apr 2026 13:42:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776001348; bh=fqKVsg5WaB6X3s8lPGP6KsCGTJsRoqueJFV5SmfwOcg=; h=Date:From:To:Cc:Subject:From; b=Awng/CEVUHQg+VVRjXaGIH3akKhWUXh8gOvePRoQx7uEnaOtLW/ll9Why9+jrloxL z/g5m+m5NuEOZs7b5nqi6ftadJa73fDRevavUWt+jUmOyd0r+AyBBeH4IV+wo/COMN raVjduua3PnsY8TqDoCaVH3PEfzvL65zqMpZQsY6Txd9J409zCvSKPezoNoS8Vdp+1 UCb6qqePkT4+v2viDkKxY62HNUKfEqPlxU/aKEXfVp1I/9Ef4NqrnkSOCcMjzHSCpH 7OKdmFRwuTDPbCxNw3VxWPPTHMS8n9BEYY6XW/gTFHeLAnTAT7977+uDhZ8S3DD0IX hKhhckG2cXpdw== Date: Sun, 12 Apr 2026 15:42:25 +0200 From: Thomas Gleixner To: Linus Torvalds Cc: linux-kernel@vger.kernel.org, x86@kernel.org Subject: [GIT pull] timers/urgent for v7.0 Message-ID: <177600132596.627834.10652158634233047399.tglx@xen13> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Linus, please pull the latest timers/urgent branch from: git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers-urgent-= 2026-04-12 up to: d6e152d905bd: clockevents: Prevent timer interrupt starvation Two fixes for the time/timers subsystem: - Invert the inverted fastpath decision in check_tick_dependency(), which prevents NOHZ full to stop the tick. That's a regression introduced in the 7.0 merge window. - Prevent a unpriviledged DoS in the clockevents code, where user space can starve the timer interrupt by arming a timerfd or posix interval timer in a tight loop with an absolute expiry time in the past. The fix turned out to be incomplete and was was amended yesterday to make it work on some 20 years old AMD machines as well. All issues with it have been confirmed to be resolved by various reporters. Thanks, tglx ------------------> Josh Snyder (1): tick/nohz: Fix inverted return value in check_tick_dependency() fast = path Thomas Gleixner (1): clockevents: Prevent timer interrupt starvation include/linux/clockchips.h | 2 ++ kernel/time/clockevents.c | 27 +++++++++++++++++++-------- kernel/time/hrtimer.c | 1 + kernel/time/tick-broadcast.c | 8 +++++++- kernel/time/tick-common.c | 1 + kernel/time/tick-sched.c | 3 ++- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index b0df28ddd394..50cdc9da8d32 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 eaae1ce9f060..38570998a19b 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 860af7a58428..1e37142fe52f 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-broadcast.c b/kernel/time/tick-broadcast.c index f63c65881364..7e57fa31ee26 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -76,8 +76,10 @@ const struct clock_event_device *tick_get_wakeup_device(= int cpu) */ static void tick_broadcast_start_periodic(struct clock_event_device *bc) { - if (bc) + if (bc) { + bc->next_event_forced =3D 0; tick_setup_periodic(bc, 1); + } } =20 /* @@ -403,6 +405,7 @@ static void tick_handle_periodic_broadcast(struct clock= _event_device *dev) bool bc_local; =20 raw_spin_lock(&tick_broadcast_lock); + tick_broadcast_device.evtdev->next_event_forced =3D 0; =20 /* Handle spurious interrupts gracefully */ if (clockevent_state_shutdown(tick_broadcast_device.evtdev)) { @@ -696,6 +699,7 @@ static void tick_handle_oneshot_broadcast(struct clock_= event_device *dev) =20 raw_spin_lock(&tick_broadcast_lock); dev->next_event =3D KTIME_MAX; + tick_broadcast_device.evtdev->next_event_forced =3D 0; next_event =3D KTIME_MAX; cpumask_clear(tmpmask); now =3D ktime_get(); @@ -1063,6 +1067,7 @@ static void tick_broadcast_setup_oneshot(struct clock= _event_device *bc, =20 =20 bc->event_handler =3D tick_handle_oneshot_broadcast; + bc->next_event_forced =3D 0; bc->next_event =3D KTIME_MAX; =20 /* @@ -1175,6 +1180,7 @@ void hotplug_cpu__broadcast_tick_pull(int deadcpu) } =20 /* This moves the broadcast assignment to this CPU: */ + bc->next_event_forced =3D 0; clockevents_program_event(bc, bc->next_event, 1); } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index d305d8521896..6a9198a4279b 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 f7907fadd63f..d1f27df1e60e 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -345,7 +345,7 @@ static bool check_tick_dependency(atomic_t *dep) int val =3D atomic_read(dep); =20 if (likely(!tracepoint_enabled(tick_stop))) - return !val; + return !!val; =20 if (val & TICK_DEP_MASK_POSIX_TIMER) { trace_tick_stop(0, TICK_DEP_MASK_POSIX_TIMER); @@ -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);