Skip to content

Commit eea3e2d

Browse files
committed
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
2 parents eda2171 + 77345ef commit eea3e2d

5 files changed

Lines changed: 67 additions & 35 deletions

File tree

arch/arm/include/asm/cpuidle.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@ extern int arm_cpuidle_suspend(int index);
4949

5050
extern int arm_cpuidle_init(int cpu);
5151

52+
struct arm_cpuidle_irq_context { };
53+
54+
#define arm_cpuidle_save_irq_context(c) (void)c
55+
#define arm_cpuidle_restore_irq_context(c) (void)c
56+
5257
#endif

arch/arm64/include/asm/cpuidle.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,39 @@ static inline int arm_cpuidle_suspend(int index)
1818
return -EOPNOTSUPP;
1919
}
2020
#endif
21+
22+
#ifdef CONFIG_ARM64_PSEUDO_NMI
23+
#include <asm/arch_gicv3.h>
24+
25+
struct arm_cpuidle_irq_context {
26+
unsigned long pmr;
27+
unsigned long daif_bits;
28+
};
29+
30+
#define arm_cpuidle_save_irq_context(__c) \
31+
do { \
32+
struct arm_cpuidle_irq_context *c = __c; \
33+
if (system_uses_irq_prio_masking()) { \
34+
c->daif_bits = read_sysreg(daif); \
35+
write_sysreg(c->daif_bits | PSR_I_BIT | PSR_F_BIT, \
36+
daif); \
37+
c->pmr = gic_read_pmr(); \
38+
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); \
39+
} \
40+
} while (0)
41+
42+
#define arm_cpuidle_restore_irq_context(__c) \
43+
do { \
44+
struct arm_cpuidle_irq_context *c = __c; \
45+
if (system_uses_irq_prio_masking()) { \
46+
gic_write_pmr(c->pmr); \
47+
write_sysreg(c->daif_bits, daif); \
48+
} \
49+
} while (0)
50+
#else
51+
struct arm_cpuidle_irq_context { };
52+
53+
#define arm_cpuidle_save_irq_context(c) (void)c
54+
#define arm_cpuidle_restore_irq_context(c) (void)c
55+
#endif
2156
#endif

arch/arm64/kernel/process.c

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
#include <linux/prctl.h>
4747

4848
#include <asm/alternative.h>
49-
#include <asm/arch_gicv3.h>
5049
#include <asm/compat.h>
5150
#include <asm/cpufeature.h>
51+
#include <asm/cpuidle.h>
5252
#include <asm/cacheflush.h>
5353
#include <asm/exec.h>
5454
#include <asm/fpsimd.h>
@@ -74,33 +74,6 @@ EXPORT_SYMBOL_GPL(pm_power_off);
7474

7575
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
7676

77-
static void noinstr __cpu_do_idle(void)
78-
{
79-
dsb(sy);
80-
wfi();
81-
}
82-
83-
static void noinstr __cpu_do_idle_irqprio(void)
84-
{
85-
unsigned long pmr;
86-
unsigned long daif_bits;
87-
88-
daif_bits = read_sysreg(daif);
89-
write_sysreg(daif_bits | PSR_I_BIT | PSR_F_BIT, daif);
90-
91-
/*
92-
* Unmask PMR before going idle to make sure interrupts can
93-
* be raised.
94-
*/
95-
pmr = gic_read_pmr();
96-
gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);
97-
98-
__cpu_do_idle();
99-
100-
gic_write_pmr(pmr);
101-
write_sysreg(daif_bits, daif);
102-
}
103-
10477
/*
10578
* cpu_do_idle()
10679
*
@@ -112,10 +85,14 @@ static void noinstr __cpu_do_idle_irqprio(void)
11285
*/
11386
void noinstr cpu_do_idle(void)
11487
{
115-
if (system_uses_irq_prio_masking())
116-
__cpu_do_idle_irqprio();
117-
else
118-
__cpu_do_idle();
88+
struct arm_cpuidle_irq_context context;
89+
90+
arm_cpuidle_save_irq_context(&context);
91+
92+
dsb(sy);
93+
wfi();
94+
95+
arm_cpuidle_restore_irq_context(&context);
11996
}
12097

12198
/*

arch/arm64/kernel/suspend.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <asm/alternative.h>
88
#include <asm/cacheflush.h>
99
#include <asm/cpufeature.h>
10+
#include <asm/cpuidle.h>
1011
#include <asm/daifflags.h>
1112
#include <asm/debug-monitors.h>
1213
#include <asm/exec.h>
@@ -91,6 +92,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
9192
int ret = 0;
9293
unsigned long flags;
9394
struct sleep_stack_data state;
95+
struct arm_cpuidle_irq_context context;
9496

9597
/* Report any MTE async fault before going to suspend */
9698
mte_suspend_enter();
@@ -103,12 +105,18 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
103105
flags = local_daif_save();
104106

105107
/*
106-
* Function graph tracer state gets incosistent when the kernel
108+
* Function graph tracer state gets inconsistent when the kernel
107109
* calls functions that never return (aka suspend finishers) hence
108110
* disable graph tracing during their execution.
109111
*/
110112
pause_graph_tracing();
111113

114+
/*
115+
* Switch to using DAIF.IF instead of PMR in order to reliably
116+
* resume if we're using pseudo-NMIs.
117+
*/
118+
arm_cpuidle_save_irq_context(&context);
119+
112120
if (__cpu_suspend_enter(&state)) {
113121
/* Call the suspend finisher */
114122
ret = fn(arg);
@@ -126,6 +134,8 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
126134
RCU_NONIDLE(__cpu_suspend_exit());
127135
}
128136

137+
arm_cpuidle_restore_irq_context(&context);
138+
129139
unpause_graph_tracing();
130140

131141
/*

drivers/firmware/psci/psci.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,15 @@ int psci_cpu_suspend_enter(u32 state)
335335
{
336336
int ret;
337337

338-
if (!psci_power_state_loses_context(state))
338+
if (!psci_power_state_loses_context(state)) {
339+
struct arm_cpuidle_irq_context context;
340+
341+
arm_cpuidle_save_irq_context(&context);
339342
ret = psci_ops.cpu_suspend(state, 0);
340-
else
343+
arm_cpuidle_restore_irq_context(&context);
344+
} else {
341345
ret = cpu_suspend(state, psci_suspend_finisher);
346+
}
342347

343348
return ret;
344349
}

0 commit comments

Comments
 (0)