Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpuidle: drivers: firmware: psci: Dont instrument suspend code

The PSCI suspend code is currently instrumentable, which is not safe as
instrumentation (e.g. ftrace) may try to make use of RCU during idle
periods when RCU is not watching.

To fix this we need to ensure that psci_suspend_finisher() and anything
it calls are not instrumented. We can do this fairly simply by marking
psci_suspend_finisher() and the psci*_cpu_suspend() functions as
noinstr, and the underlying helper functions as __always_inline.

When CONFIG_DEBUG_VIRTUAL=y, __pa_symbol() can expand to an out-of-line
instrumented function, so we must use __pa_symbol_nodebug() within
psci_suspend_finisher().

The raw SMCCC invocation functions are written in assembly, and are not
subject to compiler instrumentation.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20230126151323.349423061@infradead.org

authored by

Mark Rutland and committed by
Ingo Molnar
393e2ea3 57a30218

+19 -12
+19 -12
drivers/firmware/psci/psci.c
··· 108 108 return !(state & ~valid_mask); 109 109 } 110 110 111 - static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, 112 - unsigned long arg0, unsigned long arg1, 113 - unsigned long arg2) 111 + static __always_inline unsigned long 112 + __invoke_psci_fn_hvc(unsigned long function_id, 113 + unsigned long arg0, unsigned long arg1, 114 + unsigned long arg2) 114 115 { 115 116 struct arm_smccc_res res; 116 117 ··· 119 118 return res.a0; 120 119 } 121 120 122 - static unsigned long __invoke_psci_fn_smc(unsigned long function_id, 123 - unsigned long arg0, unsigned long arg1, 124 - unsigned long arg2) 121 + static __always_inline unsigned long 122 + __invoke_psci_fn_smc(unsigned long function_id, 123 + unsigned long arg0, unsigned long arg1, 124 + unsigned long arg2) 125 125 { 126 126 struct arm_smccc_res res; 127 127 ··· 130 128 return res.a0; 131 129 } 132 130 133 - static int psci_to_linux_errno(int errno) 131 + static __always_inline int psci_to_linux_errno(int errno) 134 132 { 135 133 switch (errno) { 136 134 case PSCI_RET_SUCCESS: ··· 171 169 return psci_to_linux_errno(err); 172 170 } 173 171 174 - static int __psci_cpu_suspend(u32 fn, u32 state, unsigned long entry_point) 172 + static __always_inline int 173 + __psci_cpu_suspend(u32 fn, u32 state, unsigned long entry_point) 175 174 { 176 175 int err; 177 176 ··· 180 177 return psci_to_linux_errno(err); 181 178 } 182 179 183 - static int psci_0_1_cpu_suspend(u32 state, unsigned long entry_point) 180 + static __always_inline int 181 + psci_0_1_cpu_suspend(u32 state, unsigned long entry_point) 184 182 { 185 183 return __psci_cpu_suspend(psci_0_1_function_ids.cpu_suspend, 186 184 state, entry_point); 187 185 } 188 186 189 - static int psci_0_2_cpu_suspend(u32 state, unsigned long entry_point) 187 + static __always_inline int 188 + psci_0_2_cpu_suspend(u32 state, unsigned long entry_point) 190 189 { 191 190 return __psci_cpu_suspend(PSCI_FN_NATIVE(0_2, CPU_SUSPEND), 192 191 state, entry_point); ··· 455 450 #endif 456 451 457 452 #ifdef CONFIG_CPU_IDLE 458 - static int psci_suspend_finisher(unsigned long state) 453 + static noinstr int psci_suspend_finisher(unsigned long state) 459 454 { 460 455 u32 power_state = state; 461 - phys_addr_t pa_cpu_resume = __pa_symbol(cpu_resume); 456 + phys_addr_t pa_cpu_resume; 457 + 458 + pa_cpu_resume = __pa_symbol_nodebug((unsigned long)cpu_resume); 462 459 463 460 return psci_ops.cpu_suspend(power_state, pa_cpu_resume); 464 461 }