Upon adjustments of the monotonic clock's frequencies from the timekeeping core, the clockevents devices' ->mult_adjusted should be changed accordingly, too.
Introduce clockevents_adjust_all_freqs() which traverses all registered clockevent devices and, if the CLOCK_EVT_FEAT_NO_ADJUST flag is not set, recalculates their ->mult_adjusted based on the monotonic clock's current frequency. Call clockevents_adjust_all_freqs() from timekeeping_apply_adjustment(). Signed-off-by: Nicolai Stange <[email protected]> --- kernel/time/clockevents.c | 33 +++++++++++++++++++++++++++++++++ kernel/time/tick-internal.h | 5 +++++ kernel/time/timekeeping.c | 3 +++ 3 files changed, 41 insertions(+) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index c8b2df8..40ee5b2 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -626,6 +626,39 @@ void __clockevents_adjust_freq(struct clock_event_device *dev) mult_cs_raw); } +void clockevents_adjust_all_freqs(u32 mult_cs_mono, u32 mult_cs_raw) +{ + u32 last_mult_raw = 0, last_shift = 0, last_mult_adjusted = 0; + u32 mult_raw, shift; + unsigned long flags; + struct clock_event_device *dev; + + raw_spin_lock_irqsave(&clockevents_lock, flags); + list_for_each_entry(dev, &clockevent_devices, list) { + if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT) || + (dev->features & CLOCK_EVT_FEAT_NO_ADJUST)) + continue; + + /* + * The cached last_mult_adjusted is only valid if + * shift == last_shift. Otherwise, it could exceed + * what is allowed by ->max_delta_ns. + */ + mult_raw = dev->mult; + shift = dev->shift; + if (mult_raw != last_mult_raw || shift != last_shift) { + last_mult_raw = mult_raw; + last_shift = shift; + last_mult_adjusted = + __clockevents_calc_adjust_freq(mult_raw, + mult_cs_mono, + mult_cs_raw); + } + dev->mult_adjusted = last_mult_adjusted; + } + raw_spin_unlock_irqrestore(&clockevents_lock, flags); +} + int __clockevents_update_freq(struct clock_event_device *dev, u32 freq) { clockevents_config(dev, freq); diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 0b29d23..2d97c42 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -56,6 +56,7 @@ extern int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, bool force); extern void clockevents_handle_noop(struct clock_event_device *dev); extern int __clockevents_update_freq(struct clock_event_device *dev, u32 freq); +extern void clockevents_adjust_all_freqs(u32 mult_cs_mono, u32 mult_cs_raw); extern void timekeeping_get_mono_mult(u32 *mult_cs_mono, u32 *mult_cs_raw); extern ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt); @@ -95,6 +96,10 @@ static inline void tick_set_periodic_handler(struct clock_event_device *dev, int #else /* !GENERIC_CLOCKEVENTS: */ static inline void tick_suspend(void) { } static inline void tick_resume(void) { } + +static inline void clockevents_adjust_all_freqs(u32 mult_cs_mono, + u32 mult_cs_raw) +{} #endif /* !GENERIC_CLOCKEVENTS */ /* Oneshot related functions */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index bd12861..6b3f2bc 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1850,6 +1850,9 @@ static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk, tk->xtime_interval += interval; tk->tkr_mono.xtime_nsec -= offset; tk->ntp_error -= (interval - offset) << tk->ntp_error_shift; + + clockevents_adjust_all_freqs(tk->tkr_mono.mult, + tk->tkr_mono.clock->mult); } /* -- 2.9.3

