diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
| -rw-r--r-- | arch/powerpc/kernel/time.c | 87 | 
1 files changed, 62 insertions, 25 deletions
| diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index cae8f03a44fe..62361cc7281c 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -88,6 +88,7 @@ static struct clocksource clocksource_timebase = {  #define DECREMENTER_DEFAULT_MAX 0x7FFFFFFF  u64 decrementer_max = DECREMENTER_DEFAULT_MAX; +EXPORT_SYMBOL_GPL(decrementer_max); /* for KVM HDEC */  static int decrementer_set_next_event(unsigned long evt,  				      struct clock_event_device *dev); @@ -107,6 +108,7 @@ struct clock_event_device decrementer_clockevent = {  EXPORT_SYMBOL(decrementer_clockevent);  DEFINE_PER_CPU(u64, decrementers_next_tb); +EXPORT_SYMBOL_GPL(decrementers_next_tb);  static DEFINE_PER_CPU(struct clock_event_device, decrementers);  #define XSEC_PER_SEC (1024*1024) @@ -496,6 +498,16 @@ EXPORT_SYMBOL(profile_pc);   * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable...   */  #ifdef CONFIG_PPC64 +static inline unsigned long test_irq_work_pending(void) +{ +	unsigned long x; + +	asm volatile("lbz %0,%1(13)" +		: "=r" (x) +		: "i" (offsetof(struct paca_struct, irq_work_pending))); +	return x; +} +  static inline void set_irq_work_pending_flag(void)  {  	asm volatile("stb %0,%1(13)" : : @@ -539,13 +551,44 @@ void arch_irq_work_raise(void)  	preempt_enable();  } +static void set_dec_or_work(u64 val) +{ +	set_dec(val); +	/* We may have raced with new irq work */ +	if (unlikely(test_irq_work_pending())) +		set_dec(1); +} +  #else  /* CONFIG_IRQ_WORK */  #define test_irq_work_pending()	0  #define clear_irq_work_pending() +static void set_dec_or_work(u64 val) +{ +	set_dec(val); +}  #endif /* CONFIG_IRQ_WORK */ +#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE +void timer_rearm_host_dec(u64 now) +{ +	u64 *next_tb = this_cpu_ptr(&decrementers_next_tb); + +	WARN_ON_ONCE(!arch_irqs_disabled()); +	WARN_ON_ONCE(mfmsr() & MSR_EE); + +	if (now >= *next_tb) { +		local_paca->irq_happened |= PACA_IRQ_DEC; +	} else { +		now = *next_tb - now; +		if (now <= decrementer_max) +			set_dec_or_work(now); +	} +} +EXPORT_SYMBOL_GPL(timer_rearm_host_dec); +#endif +  /*   * timer_interrupt - gets called when the decrementer overflows,   * with interrupts disabled. @@ -566,22 +609,23 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt)  		return;  	} -	/* Ensure a positive value is written to the decrementer, or else -	 * some CPUs will continue to take decrementer exceptions. When the -	 * PPC_WATCHDOG (decrementer based) is configured, keep this at most -	 * 31 bits, which is about 4 seconds on most systems, which gives -	 * the watchdog a chance of catching timer interrupt hard lockups. -	 */ -	if (IS_ENABLED(CONFIG_PPC_WATCHDOG)) -		set_dec(0x7fffffff); -	else -		set_dec(decrementer_max); - -	/* Conditionally hard-enable interrupts now that the DEC has been -	 * bumped to its maximum value -	 */ -	may_hard_irq_enable(); +	/* Conditionally hard-enable interrupts. */ +	if (should_hard_irq_enable()) { +		/* +		 * Ensure a positive value is written to the decrementer, or +		 * else some CPUs will continue to take decrementer exceptions. +		 * When the PPC_WATCHDOG (decrementer based) is configured, +		 * keep this at most 31 bits, which is about 4 seconds on most +		 * systems, which gives the watchdog a chance of catching timer +		 * interrupt hard lockups. +		 */ +		if (IS_ENABLED(CONFIG_PPC_WATCHDOG)) +			set_dec(0x7fffffff); +		else +			set_dec(decrementer_max); +		do_hard_irq_enable(); +	}  #if defined(CONFIG_PPC32) && defined(CONFIG_PPC_PMAC)  	if (atomic_read(&ppc_n_lost_interrupts) != 0) @@ -606,10 +650,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt)  	} else {  		now = *next_tb - now;  		if (now <= decrementer_max) -			set_dec(now); -		/* We may have raced with new irq work */ -		if (test_irq_work_pending()) -			set_dec(1); +			set_dec_or_work(now);  		__this_cpu_inc(irq_stat.timer_irqs_others);  	} @@ -730,7 +771,7 @@ static int __init get_freq(char *name, int cells, unsigned long *val)  static void start_cpu_decrementer(void)  { -#if defined(CONFIG_BOOKE) || defined(CONFIG_40x) +#ifdef CONFIG_BOOKE_OR_40x  	unsigned int tcr;  	/* Clear any pending timer interrupts */ @@ -843,11 +884,7 @@ static int decrementer_set_next_event(unsigned long evt,  				      struct clock_event_device *dev)  {  	__this_cpu_write(decrementers_next_tb, get_tb() + evt); -	set_dec(evt); - -	/* We may have raced with new irq work */ -	if (test_irq_work_pending()) -		set_dec(1); +	set_dec_or_work(evt);  	return 0;  } |