Commit 19f8a5b5 authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman

powerpc/powernv: Don't reprogram SLW image on every KVM guest entry/exit

Commit 24be85a2 ("powerpc/powernv: Clear PECE1 in LPCR via stop-api
only on Hotplug", 2017-07-21) added two calls to opal_slw_set_reg()
inside pnv_cpu_offline(), with the aim of changing the LPCR value in
the SLW image to disable wakeups from the decrementer while a CPU is
offline.  However, pnv_cpu_offline() gets called each time a secondary
CPU thread is woken up to participate in running a KVM guest, that is,
not just when a CPU is offlined.

Since opal_slw_set_reg() is a very slow operation (with observed
execution times around 20 milliseconds), this means that an offline
secondary CPU can often be busy doing the opal_slw_set_reg() call
when the primary CPU wants to grab all the secondary threads so that
it can run a KVM guest.  This leads to messages like "KVM: couldn't
grab CPU n" being printed and guest execution failing.

There is no need to reprogram the SLW image on every KVM guest entry
and exit.  So that we do it only when a CPU is really transitioning
between online and offline, this moves the calls to
pnv_program_cpu_hotplug_lpcr() into pnv_smp_cpu_kill_self().

Fixes: 24be85a2 ("powerpc/powernv: Clear PECE1 in LPCR via stop-api only on Hotplug")
Cc: stable@vger.kernel.org # v4.14+
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 8cfaf106
...@@ -23,6 +23,8 @@ extern int pnv_npu2_handle_fault(struct npu_context *context, uintptr_t *ea, ...@@ -23,6 +23,8 @@ extern int pnv_npu2_handle_fault(struct npu_context *context, uintptr_t *ea,
unsigned long *flags, unsigned long *status, unsigned long *flags, unsigned long *status,
int count); int count);
void pnv_program_cpu_hotplug_lpcr(unsigned int cpu, u64 lpcr_val);
void pnv_tm_init(void); void pnv_tm_init(void);
#else #else
static inline void powernv_set_nmmu_ptcr(unsigned long ptcr) { } static inline void powernv_set_nmmu_ptcr(unsigned long ptcr) { }
......
...@@ -458,7 +458,8 @@ EXPORT_SYMBOL_GPL(pnv_power9_force_smt4_release); ...@@ -458,7 +458,8 @@ EXPORT_SYMBOL_GPL(pnv_power9_force_smt4_release);
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */ #endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
static void pnv_program_cpu_hotplug_lpcr(unsigned int cpu, u64 lpcr_val)
void pnv_program_cpu_hotplug_lpcr(unsigned int cpu, u64 lpcr_val)
{ {
u64 pir = get_hard_smp_processor_id(cpu); u64 pir = get_hard_smp_processor_id(cpu);
...@@ -481,20 +482,6 @@ unsigned long pnv_cpu_offline(unsigned int cpu) ...@@ -481,20 +482,6 @@ unsigned long pnv_cpu_offline(unsigned int cpu)
{ {
unsigned long srr1; unsigned long srr1;
u32 idle_states = pnv_get_supported_cpuidle_states(); u32 idle_states = pnv_get_supported_cpuidle_states();
u64 lpcr_val;
/*
* We don't want to take decrementer interrupts while we are
* offline, so clear LPCR:PECE1. We keep PECE2 (and
* LPCR_PECE_HVEE on P9) enabled as to let IPIs in.
*
* If the CPU gets woken up by a special wakeup, ensure that
* the SLW engine sets LPCR with decrementer bit cleared, else
* the CPU will come back to the kernel due to a spurious
* wakeup.
*/
lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1;
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
__ppc64_runlatch_off(); __ppc64_runlatch_off();
...@@ -526,16 +513,6 @@ unsigned long pnv_cpu_offline(unsigned int cpu) ...@@ -526,16 +513,6 @@ unsigned long pnv_cpu_offline(unsigned int cpu)
__ppc64_runlatch_on(); __ppc64_runlatch_on();
/*
* Re-enable decrementer interrupts in LPCR.
*
* Further, we want stop states to be woken up by decrementer
* for non-hotplug cases. So program the LPCR via stop api as
* well.
*/
lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1;
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
return srr1; return srr1;
} }
#endif #endif
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/kexec.h> #include <asm/kexec.h>
#include <asm/reg.h> #include <asm/reg.h>
#include <asm/powernv.h>
#include "powernv.h" #include "powernv.h"
...@@ -153,6 +154,7 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -153,6 +154,7 @@ static void pnv_smp_cpu_kill_self(void)
{ {
unsigned int cpu; unsigned int cpu;
unsigned long srr1, wmask; unsigned long srr1, wmask;
u64 lpcr_val;
/* Standard hot unplug procedure */ /* Standard hot unplug procedure */
/* /*
...@@ -174,6 +176,19 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -174,6 +176,19 @@ static void pnv_smp_cpu_kill_self(void)
if (cpu_has_feature(CPU_FTR_ARCH_207S)) if (cpu_has_feature(CPU_FTR_ARCH_207S))
wmask = SRR1_WAKEMASK_P8; wmask = SRR1_WAKEMASK_P8;
/*
* We don't want to take decrementer interrupts while we are
* offline, so clear LPCR:PECE1. We keep PECE2 (and
* LPCR_PECE_HVEE on P9) enabled so as to let IPIs in.
*
* If the CPU gets woken up by a special wakeup, ensure that
* the SLW engine sets LPCR with decrementer bit cleared, else
* the CPU will come back to the kernel due to a spurious
* wakeup.
*/
lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1;
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
while (!generic_check_cpu_restart(cpu)) { while (!generic_check_cpu_restart(cpu)) {
/* /*
* Clear IPI flag, since we don't handle IPIs while * Clear IPI flag, since we don't handle IPIs while
...@@ -246,6 +261,16 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -246,6 +261,16 @@ static void pnv_smp_cpu_kill_self(void)
} }
/*
* Re-enable decrementer interrupts in LPCR.
*
* Further, we want stop states to be woken up by decrementer
* for non-hotplug cases. So program the LPCR via stop api as
* well.
*/
lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1;
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
DBG("CPU%d coming online...\n", cpu); DBG("CPU%d coming online...\n", cpu);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment