]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
clocksource: Update clocksource::freq_khz on registration
authorThomas Gleixner <tglx@kernel.org>
Wed, 4 Mar 2026 18:49:29 +0000 (19:49 +0100)
committerThomas Gleixner <tglx@kernel.org>
Thu, 5 Mar 2026 16:41:06 +0000 (17:41 +0100)
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
kernel/time/clocksource.c

index df719496165849aee21730886ad497822201f579..3c205447717ae2050161a0f22b2d133de06850b3 100644 (file)
@@ -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",