aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@kernel.org>2026-03-04 19:49:29 +0100
committerThomas Gleixner <tglx@kernel.org>2026-03-05 17:41:06 +0100
commit53007d526e17d29f0e5b81c07eb594a93bc4d29c (patch)
treed2f8c544a354a60125fea0ebb7ea59c26b19ff34 /kernel
parent9d5e25b361b7228b422fd32bd1c327fd7fb919b4 (diff)
clocksource: Update clocksource::freq_khz on registration
Borislav reported a division by zero in the timekeeping code and random hangs with the new coupled clocksource/clockevent functionality. It turned out that the TSC clocksource is not always updating the freq_khz field of the clocksource on registration. The coupled mode conversion calculation requires the frequency and as it's not initialized the resulting factor is zero or a random value. As a consequence this causes a division by zero or random boot hangs. Instead of chasing down all clocksources which fail to update that member, fill it in at registration time where the caller has to supply the frequency anyway. Except for special clocksources like jiffies which never can have coupled mode. To make this more robust put a check into the registration function to validate that the caller supplied a frequency if the coupled mode feature bit is set. If not, emit a warning and clear the feature bit. Fixes: cd38bdb8e696 ("timekeeping: Provide infrastructure for coupled clockevents") Reported-by: Borislav Petkov <bp@alien8.de> Reported-by: Nathan Chancellor <nathan@kernel.org> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Tested-by: Borislav Petkov <bp@alien8.de> Tested-by: Nathan Chancellor <nathan@kernel.org> Link: https://patch.msgid.link/87cy1jsa4m.ffs@tglx Closes: https://lore.kernel.org/20260303213027.GA2168957@ax162
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/clocksource.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index df7194961658..3c205447717a 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -1169,6 +1169,9 @@ void __clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq
clocks_calc_mult_shift(&cs->mult, &cs->shift, freq,
NSEC_PER_SEC / scale, sec * scale);
+
+ /* Update cs::freq_khz */
+ cs->freq_khz = div_u64((u64)freq * scale, 1000);
}
/*
@@ -1241,6 +1244,10 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
if (WARN_ON_ONCE((unsigned int)cs->id >= CSID_MAX))
cs->id = CSID_GENERIC;
+
+ if (WARN_ON_ONCE(!freq && cs->flags & CLOCK_SOURCE_HAS_COUPLED_CLOCK_EVENT))
+ cs->flags &= ~CLOCK_SOURCE_HAS_COUPLED_CLOCK_EVENT;
+
if (cs->vdso_clock_mode < 0 ||
cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) {
pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n",