Merge branch 'for-next/cpuidle' into for-next/core

Fix resume from idle when pNMI is being used.

* for-next/cpuidle:
  arm64: suspend: Use cpuidle context helpers in cpu_suspend()
  PSCI: Use cpuidle context helpers in psci_cpu_suspend_enter()
  arm64: Convert cpu_do_idle() to using cpuidle context helpers
  arm64: Add cpuidle context save/restore helpers
This commit is contained in:
Will Deacon 2021-06-24 13:36:39 +01:00
commit eea3e2dec4
5 changed files with 67 additions and 35 deletions

View File

@ -49,4 +49,9 @@ extern int arm_cpuidle_suspend(int index);
extern int arm_cpuidle_init(int cpu);
struct arm_cpuidle_irq_context { };
#define arm_cpuidle_save_irq_context(c) (void)c
#define arm_cpuidle_restore_irq_context(c) (void)c
#endif

View File

@ -18,4 +18,39 @@ static inline int arm_cpuidle_suspend(int index)
return -EOPNOTSUPP;
}
#endif
#ifdef CONFIG_ARM64_PSEUDO_NMI
#include <asm/arch_gicv3.h>
struct arm_cpuidle_irq_context {
unsigned long pmr;
unsigned long daif_bits;
};
#define arm_cpuidle_save_irq_context(__c) \
do { \
struct arm_cpuidle_irq_context *c = __c; \
if (system_uses_irq_prio_masking()) { \
c->daif_bits = read_sysreg(daif); \
write_sysreg(c->daif_bits | PSR_I_BIT | PSR_F_BIT, \
daif); \
c->pmr = gic_read_pmr(); \
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); \
} \
} while (0)
#define arm_cpuidle_restore_irq_context(__c) \
do { \
struct arm_cpuidle_irq_context *c = __c; \
if (system_uses_irq_prio_masking()) { \
gic_write_pmr(c->pmr); \
write_sysreg(c->daif_bits, daif); \
} \
} while (0)
#else
struct arm_cpuidle_irq_context { };
#define arm_cpuidle_save_irq_context(c) (void)c
#define arm_cpuidle_restore_irq_context(c) (void)c
#endif
#endif

View File

@ -46,9 +46,9 @@
#include <linux/prctl.h>
#include <asm/alternative.h>
#include <asm/arch_gicv3.h>
#include <asm/compat.h>
#include <asm/cpufeature.h>
#include <asm/cpuidle.h>
#include <asm/cacheflush.h>
#include <asm/exec.h>
#include <asm/fpsimd.h>
@ -74,33 +74,6 @@ EXPORT_SYMBOL_GPL(pm_power_off);
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
static void noinstr __cpu_do_idle(void)
{
dsb(sy);
wfi();
}
static void noinstr __cpu_do_idle_irqprio(void)
{
unsigned long pmr;
unsigned long daif_bits;
daif_bits = read_sysreg(daif);
write_sysreg(daif_bits | PSR_I_BIT | PSR_F_BIT, daif);
/*
* Unmask PMR before going idle to make sure interrupts can
* be raised.
*/
pmr = gic_read_pmr();
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
__cpu_do_idle();
gic_write_pmr(pmr);
write_sysreg(daif_bits, daif);
}
/*
* cpu_do_idle()
*
@ -112,10 +85,14 @@ static void noinstr __cpu_do_idle_irqprio(void)
*/
void noinstr cpu_do_idle(void)
{
if (system_uses_irq_prio_masking())
__cpu_do_idle_irqprio();
else
__cpu_do_idle();
struct arm_cpuidle_irq_context context;
arm_cpuidle_save_irq_context(&context);
dsb(sy);
wfi();
arm_cpuidle_restore_irq_context(&context);
}
/*

View File

@ -7,6 +7,7 @@
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/cpuidle.h>
#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
#include <asm/exec.h>
@ -91,6 +92,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
int ret = 0;
unsigned long flags;
struct sleep_stack_data state;
struct arm_cpuidle_irq_context context;
/* Report any MTE async fault before going to suspend */
mte_suspend_enter();
@ -103,12 +105,18 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
flags = local_daif_save();
/*
* Function graph tracer state gets incosistent when the kernel
* Function graph tracer state gets inconsistent when the kernel
* calls functions that never return (aka suspend finishers) hence
* disable graph tracing during their execution.
*/
pause_graph_tracing();
/*
* Switch to using DAIF.IF instead of PMR in order to reliably
* resume if we're using pseudo-NMIs.
*/
arm_cpuidle_save_irq_context(&context);
if (__cpu_suspend_enter(&state)) {
/* Call the suspend finisher */
ret = fn(arg);
@ -126,6 +134,8 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
RCU_NONIDLE(__cpu_suspend_exit());
}
arm_cpuidle_restore_irq_context(&context);
unpause_graph_tracing();
/*

View File

@ -335,10 +335,15 @@ int psci_cpu_suspend_enter(u32 state)
{
int ret;
if (!psci_power_state_loses_context(state))
if (!psci_power_state_loses_context(state)) {
struct arm_cpuidle_irq_context context;
arm_cpuidle_save_irq_context(&context);
ret = psci_ops.cpu_suspend(state, 0);
else
arm_cpuidle_restore_irq_context(&context);
} else {
ret = cpu_suspend(state, psci_suspend_finisher);
}
return ret;
}