[PATCH v2 0/3] Fix NXP STM clockevent bugs causing lockups
flat view
HOTtoday
From: Enric Balletbo i Serra <hidden>
Date: 2026-11-02 21:16:41
Also in:
linux-rt-devel, lkml, stable
Subsystem:
clocksource, clockevent drivers, the rest · Maintainers:
Daniel Lezcano, Thomas Gleixner, Linus Torvalds
This series fixes three bugs in the NXP STM clocksource driver that can cause soft lockups and missed timer events, particularly visible on PREEMPT_RT kernels running on S32G platforms. The NXP STM generates an interrupt only on exact match between the counter and comparator registers. If the counter passes the programmed value, the match is missed and the hardware must count through all 2^32 ticks (~32.2s at 133 MHz) before firing, causing soft lockups. Patch 1 fixes max_delta: on 64-bit platforms, ULONG_MAX exceeds the hardware's 32-bit compare register width, making it possible to program an unreachable compare value. Patch 2 fixes the overshoot detection in set_next_event: the existing check only detected counter wraps, not the normal case where the counter advances past the programmed value during the read-modify-write window. Replace it with a signed comparison following the pattern used by timer-imx-gpt. Patch 3 increases min_delta from 2us to 20us: under bus contention on PREEMPT_RT, 2us is not enough margin and the counter frequently overshoots the programmed value, triggering repeated -ETIME retries. The lockup can be reproduced on an unpatched S32G board with: # ./trigger_lockup & # stress-ng --matrix 0 --memthrash 0 & dmesg -w | grep -i "soft lockup" Where trigger_lockup.c is: #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <time.h> #include <pthread.h> #include <sched.h> #include <unistd.h> void *timer_hammer(void *arg) { long cpu = (long)arg; cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); /* Request 1 ns to force the kernel to clamp to min_delta (2 us) */ struct timespec req = { .tv_sec = 0, .tv_nsec = 1 }; while (1) { clock_nanosleep(CLOCK_MONOTONIC, 0, &req, NULL); } return NULL; } int main(void) { long ncpus = sysconf(_SC_NPROCESSORS_ONLN); pthread_t threads[ncpus]; printf("[+] Hammering hrtimers with minimal deltas across %ld cores...\n", ncpus); printf("[+] Check dmesg for: 'BUG: soft lockup - CPU#... stuck for 32s!'\n"); for (long i = 0; i < ncpus; i++) { pthread_create(&threads[i], NULL, timer_hammer, (void *)i); } for (long i = 0; i < ncpus; i++) { pthread_join(threads[i], NULL); } return 0; } The soft lockup trace looks like: watchdog: BUG: soft lockup - CPU#1 stuck for 32s! [swapper/1:0] Modules linked in: nft_ct nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_masq nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nf_tables veth bridge stp llc rfkill binfmt_misc nls_ascii nls_cp437 vfat fat ina2xx rtc_pcf85063 regmap_spi micrel phy_package aquantia crc_itu_t ci_hdrc_imx dwmac_s32 crct10dif_ce ci_hdrc ghash_ce stmmac_platform sha2_ce sha256_arm64 sha1_ce udc_core stmmac ulpi phy_generic pcs_xpcs usbmisc_imx phylink ptp pps_core i2c_imx fsl_edma virt_dma flexcan can_dev s32g_wdt watchdog sch_fq_codel drm fuse drm_panel_orientation_quirks nfnetlink dmi_sysfs ipv6 CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Tainted: G X ------ --- 6.12.0-274.el10iv.aarch64 #1 PREEMPT_RT Tainted: [X]=AUX Hardware name: Unknown Unknown Product/Unknown Product, BIOS 2022.04-15.el10_4 04/01/2022 pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : default_idle_call+0x3c/0x170 lr : default_idle_call+0x30/0x170 sp : ffff80008011bdd0 x29: ffff80008011bdd0 x28: 0000000000000000 x27: 0000000000000000 x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000 x23: 00000000000000c0 x22: ffff0008803ac840 x21: ffff0008803ac840 x20: 0000000000000000 x19: 0000000000000000 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 003369266b813144 x13: 0000000000000000 x12: 00000000000002eb x11: 00000000000000c0 x10: 00000000000010a0 x9 : ffff800080350ff4 x8 : ffff0008803ad940 x7 : 0000000000000000 x6 : 0000000000000000 x5 : 0000000000000004 x4 : 0000000000c1c684 x3 : 0000000000000001 x2 : ffff0008803ac840 x1 : ffff800081712628 x0 : 0000000000000001 Call trace: default_idle_call+0x3c/0x170 (P) cpuidle_idle_call+0x1a8/0x248 do_idle+0xa4/0x118 cpu_startup_entry+0x3c/0x50 secondary_start_kernel+0x124/0x168 __secondary_switched+0xc0/0xc8 hrtimer: interrupt took 7246 ns Signed-off-by: Enric Balletbo i Serra <redacted> --- Changes in v2: - Update the code comment in nxp_stm_clockevent_starting_cpu() and the commit message: clarify that 20us prevents repeated -ETIME retry latency, removing the misleading reference to 32s wraparounds. - Link to v1: https://lore.kernel.org/r/20261026-nxp-stm-fixes-v1-0-20a64a4ea454@redhat.com (local) --- Enric Balletbo i Serra (3): clocksource/drivers/nxp-stm: Fix max_delta overflow on 64-bit platforms clocksource/drivers/nxp-stm: Fix missed comparator match in set_next_event clocksource/drivers/nxp-stm: Increase min_delta to 20us for PREEMPT_RT drivers/clocksource/timer-nxp-stm.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) --- base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4 change-id: 20261023-nxp-stm-fixes-ab0163517e6f Best regards, -- Enric Balletbo i Serra [off-list ref]