timer fixes
Subsystems (5)
Linus,
Please pull the latest timers-urgent-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers-urgent-for-linus
# HEAD: 22cc1ca3c5469cf17e149be232817b9223afa5e4 x86/hpet: Fix /dev/rtc breakage caused by RTC cleanup
Misc fixes: a /dev/rtc regression fix, two APIC timer period calibration fixes, an ARM clocksource driver fix and a NOHZ power use regression fix.
Thanks,
Ingo
------------------>
Arnd Bergmann (1):
x86/hpet: Fix /dev/rtc breakage caused by RTC cleanup
Chris Metcalf (1):
timers: Fix get_next_timer_interrupt() computation
Marc Zyngier (1):
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
Nicolai Stange (2):
x86/timers/apic: Fix imprecise timer interrupts by eliminating TSC clockevents frequency roundoff error x86/timers/apic: Inform TSC deadline clockevent device about recalibration
arch/x86/include/asm/apic.h | 2 ++ arch/x86/kernel/apic/apic.c | 28 ++++++++++++++++++++++++++-- arch/x86/kernel/hpet.c | 2 +- arch/x86/kernel/tsc.c | 4 ++++ drivers/clocksource/arm_arch_timer.c | 26 +++++++++++++++++++++++--- kernel/time/timer.c | 5 ++++- 6 files changed, 60 insertions(+), 7 deletions(-)
arch/x86/include/asm/apic.h +2 -0
@@ -135,6 +135,7 @@ extern void init_apic_mappings(void);void register_lapic_address (unsigned long address);extern void setup_boot_APIC_clock (void );extern void setup_secondary_APIC_clock (void ); +extern void lapic_update_tsc_freq (void );extern int APIC_init_uniprocessor (void );#ifdef CONFIG_X86_64
@@ -170,6 +171,7 @@ static inline void init_apic_mappings(void) { }static inline void disable_local_APIC (void ) { }# define setup_boot_APIC_clock x86_init_noop# define setup_secondary_APIC_clock x86_init_noop +static inline void lapic_update_tsc_freq (void ) { }#endif /* !CONFIG_X86_LOCAL_APIC */ #ifdef CONFIG_X86_X2APIC
arch/x86/kernel/apic/apic.c +26 -2
@@ -313,7 +313,7 @@ int lapic_get_maxlvt(void)/* Clock divisor */ #define APIC_DIVISOR 16 -#define TSC_DIVISOR 32 +#define TSC_DIVISOR 8/* * This function sets up the local APIC timer, with a timeout of
@@ -565,13 +565,37 @@ static void setup_APIC_timer(void) CLOCK_EVT_FEAT_DUMMY); levt->set_next_event = lapic_next_deadline;clockevents_config_and_register (levt, - (tsc_khz /TSC_DIVISOR ) *1000 , + tsc_khz * (1000 /TSC_DIVISOR ),0xF , ~0UL ); }else clockevents_register_device (levt); }/* +* Install the updated TSC frequency from recalibration at the TSC +* deadline clockevent devices. +*/ +static void __lapic_update_tsc_freq (void *info) +{ +struct clock_event_device *levt =this_cpu_ptr (&lapic_events); + +if (!this_cpu_has (X86_FEATURE_TSC_DEADLINE_TIMER )) +return ; + +clockevents_update_freq (levt, tsc_khz * (1000 /TSC_DIVISOR )); +} + +void lapic_update_tsc_freq (void ) +{ +/* +* The clockevent device's ->mult and ->shift can both be +* changed. In order to avoid races, schedule the frequency +* update code on each CPU. +*/ +on_each_cpu (__lapic_update_tsc_freq,NULL ,0 ); +} + +/* * In this functions we calibrate APIC bus clocks to the external timer. * * We want to do the calibration only once since we want to have local timer
arch/x86/kernel/hpet.c +1 -1
@@ -1242,7 +1242,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)memset (&curr_time,0 ,sizeof (struct rtc_time ));if (hpet_rtc_flags & (RTC_UIE |RTC_AIE )) -mc146818_set_time (&curr_time); +mc146818_get_time (&curr_time);if (hpet_rtc_flags &RTC_UIE && curr_time.tm_sec != hpet_prev_update_sec) {
arch/x86/kernel/tsc.c +4 -0
@@ -22,6 +22,7 @@#include <asm/nmi.h> #include <asm/x86_init.h> #include <asm/geode.h> +#include <asm/apic.h> unsigned int __read_mostly cpu_khz;/* TSC clocks / usec, not used here */ EXPORT_SYMBOL (cpu_khz);
@@ -1249,6 +1250,9 @@ static void tsc_refine_calibration_work(struct work_struct *work) (unsigned long )tsc_khz /1000 , (unsigned long)tsc_khz % 1000); +/* Inform the TSC deadline clockevent devices about the recalibration */ +lapic_update_tsc_freq (); + out:if (boot_cpu_has (X86_FEATURE_ART )) art_related_clocksource = &clocksource_tsc;
drivers/clocksource/arm_arch_timer.c +23 -3
@@ -8,6 +8,9 @@* it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt (fmt) "arm_arch_timer: " fmt +#include <linux/init.h> #include <linux/kernel.h> #include <linux/device.h>
@@ -370,16 +373,33 @@ static bool arch_timer_has_nonsecure_ppi(void) arch_timer_ppi[PHYS_NONSECURE_PPI]); } +static u32 check_ppi_trigger (int irq) +{ +u32 flags =irq_get_trigger_type (irq); + +if (flags !=IRQF_TRIGGER_HIGH && flags !=IRQF_TRIGGER_LOW ) { +pr_warn ("WARNING: Invalid trigger for IRQ%d, assuming level low\n" , irq); +pr_warn ("WARNING: Please fix your firmware\n" ); + flags =IRQF_TRIGGER_LOW ; + } + +return flags; +} +static int arch_timer_starting_cpu (unsigned int cpu) {struct clock_event_device *clk =this_cpu_ptr (arch_timer_evt); +u32 flags;__arch_timer_setup (ARCH_CP15_TIMER , clk); -enable_percpu_irq (arch_timer_ppi[arch_timer_uses_ppi],0 ); + flags =check_ppi_trigger (arch_timer_ppi[arch_timer_uses_ppi]); +enable_percpu_irq (arch_timer_ppi[arch_timer_uses_ppi], flags); -if (arch_timer_has_nonsecure_ppi ()) -enable_percpu_irq (arch_timer_ppi[PHYS_NONSECURE_PPI ],0 ); +if (arch_timer_has_nonsecure_ppi ()) { + flags =check_ppi_trigger (arch_timer_ppi[PHYS_NONSECURE_PPI ]); +enable_percpu_irq (arch_timer_ppi[PHYS_NONSECURE_PPI ], flags); + }arch_counter_set_user_access ();if (evtstrm_enable)
kernel/time/timer.c +4 -1
@@ -1496,6 +1496,7 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem)struct timer_base *base =this_cpu_ptr (&timer_bases[BASE_STD ]);u64 expires =KTIME_MAX ;unsigned long nextevt; +bool is_max_delta;/* * Pretend that there is no timer pending if the cpu is offline.
@@ -1506,6 +1507,7 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem)spin_lock (&base->lock); nextevt =__next_timer_interrupt (base); + is_max_delta = (nextevt == base->clk +NEXT_TIMER_MAX_DELTA ); base->next_expiry = nextevt;/* * We have a fresh next event. Check whether we can forward the base:
@@ -1519,7 +1521,8 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem) expires = basem; base->is_idle = false; }else { - expires = basem + (nextevt - basej) *TICK_NSEC ; +if (!is_max_delta) + expires = basem + (nextevt - basej) *TICK_NSEC ;/* * If we expect to sleep more than a tick, mark the base idle: */