When CONFIG_RANDOM_KMALLOC_CACHES or other randomization infrastructrue enabled, the idle_task's stack may different between the booting kernel and target kernel. So when resuming from hibernation, an ACTION_BOOT_CPU IPI wakeup the idle instruction in arch_cpu_idle_dead() and jump to the interrupt handler. But since the stack pointer is changed, the interrupt handler cannot restore correct context.
So rename the current arch_cpu_idle_dead() to idle_play_dead(), make it as the default version of play_dead(), and the new arch_cpu_idle_dead() call play_dead() directly. For hibernation, implement an arch-specific hibernate_resume_nonboot_cpu_disable() to use the polling version (idle instruction is replace by nop, and irq is disabled) of play_dead(), i.e. poll_play_dead(), to avoid IPI handler corrupting the idle_task's stack when resuming from hibernation.
This solution is a little similar to commit 406f992e4a372dafbe3c ("x86 / hibernate: Use hlt_play_dead() when resuming from hibernation").
Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen chenhuacai@loongson.cn --- arch/loongarch/kernel/smp.c | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index fbf747447f13..308478f29278 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -19,6 +19,7 @@ #include <linux/smp.h> #include <linux/threads.h> #include <linux/export.h> +#include <linux/suspend.h> #include <linux/syscore_ops.h> #include <linux/time.h> #include <linux/tracepoint.h> @@ -423,7 +424,7 @@ void loongson_cpu_die(unsigned int cpu) mb(); }
-void __noreturn arch_cpu_idle_dead(void) +static void __noreturn idle_play_dead(void) { register uint64_t addr; register void (*init_fn)(void); @@ -447,6 +448,43 @@ void __noreturn arch_cpu_idle_dead(void) BUG(); }
+static void __noreturn poll_play_dead(void) +{ + register uint64_t addr; + register void (*init_fn)(void); + + idle_task_exit(); + __this_cpu_write(cpu_state, CPU_DEAD); + + __smp_mb(); + do { + __asm__ __volatile__("nop\n\t"); + addr = iocsr_read64(LOONGARCH_IOCSR_MBUF0); + } while (addr == 0); + + init_fn = (void *)TO_CACHE(addr); + iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_CLEAR); + + init_fn(); + BUG(); +} + +static void (*play_dead)(void) = idle_play_dead; + +void __noreturn arch_cpu_idle_dead(void) +{ + play_dead(); + BUG(); /* play_dead() doesn't return */ +} + +#ifdef CONFIG_HIBERNATION +int hibernate_resume_nonboot_cpu_disable(void) +{ + play_dead = poll_play_dead; + return suspend_disable_secondary_cpus(); +} +#endif + #endif
/*
Hi Huacai,
kernel test robot noticed the following build warnings:
[auto build test WARNING on linus/master] [also build test WARNING on v6.14-rc4 next-20250227] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Huacai-Chen/LoongArch-Use-pol... base: linus/master patch link: https://lore.kernel.org/r/20250225111812.3065545-1-chenhuacai%40loongson.cn patch subject: [PATCH] LoongArch: Use polling play_dead() when resuming from hibernation config: loongarch-randconfig-001-20250227 (https://download.01.org/0day-ci/archive/20250228/202502280356.YjzMIJ8n-lkp@i...) compiler: loongarch64-linux-gcc (GCC) 14.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250228/202502280356.YjzMIJ8n-lkp@i...)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202502280356.YjzMIJ8n-lkp@intel.com/
All warnings (new ones prefixed by >>):
arch/loongarch/kernel/smp.c:451:24: warning: 'poll_play_dead' defined but not used [-Wunused-function]
451 | static void __noreturn poll_play_dead(void) | ^~~~~~~~~~~~~~
vim +/poll_play_dead +451 arch/loongarch/kernel/smp.c
450
451 static void __noreturn poll_play_dead(void)
452 { 453 register uint64_t addr; 454 register void (*init_fn)(void); 455 456 idle_task_exit(); 457 __this_cpu_write(cpu_state, CPU_DEAD); 458 459 __smp_mb(); 460 do { 461 __asm__ __volatile__("nop\n\t"); 462 addr = iocsr_read64(LOONGARCH_IOCSR_MBUF0); 463 } while (addr == 0); 464 465 init_fn = (void *)TO_CACHE(addr); 466 iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_CLEAR); 467 468 init_fn(); 469 BUG(); 470 } 471
linux-stable-mirror@lists.linaro.org