Skip to content

Commit c911743

Browse files
committed
LoongArch: Use polling play_dead() when resuming from hibernation
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 406f992 ("x86 / hibernate: Use hlt_play_dead() when resuming from hibernation"). Cc: stable@vger.kernel.org Tested-by: Erpeng Xu <xuerpeng@uniontech.com> Tested-by: Yuli Wang <wangyuli@uniontech.com> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent a0d3c8b commit c911743

File tree

1 file changed

+46
-1
lines changed
  • arch/loongarch/kernel

1 file changed

+46
-1
lines changed

arch/loongarch/kernel/smp.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/smp.h>
2020
#include <linux/threads.h>
2121
#include <linux/export.h>
22+
#include <linux/suspend.h>
2223
#include <linux/syscore_ops.h>
2324
#include <linux/time.h>
2425
#include <linux/tracepoint.h>
@@ -423,7 +424,7 @@ void loongson_cpu_die(unsigned int cpu)
423424
mb();
424425
}
425426

426-
void __noreturn arch_cpu_idle_dead(void)
427+
static void __noreturn idle_play_dead(void)
427428
{
428429
register uint64_t addr;
429430
register void (*init_fn)(void);
@@ -447,6 +448,50 @@ void __noreturn arch_cpu_idle_dead(void)
447448
BUG();
448449
}
449450

451+
#ifdef CONFIG_HIBERNATION
452+
static void __noreturn poll_play_dead(void)
453+
{
454+
register uint64_t addr;
455+
register void (*init_fn)(void);
456+
457+
idle_task_exit();
458+
__this_cpu_write(cpu_state, CPU_DEAD);
459+
460+
__smp_mb();
461+
do {
462+
__asm__ __volatile__("nop\n\t");
463+
addr = iocsr_read64(LOONGARCH_IOCSR_MBUF0);
464+
} while (addr == 0);
465+
466+
init_fn = (void *)TO_CACHE(addr);
467+
iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_CLEAR);
468+
469+
init_fn();
470+
BUG();
471+
}
472+
#endif
473+
474+
static void (*play_dead)(void) = idle_play_dead;
475+
476+
void __noreturn arch_cpu_idle_dead(void)
477+
{
478+
play_dead();
479+
BUG(); /* play_dead() doesn't return */
480+
}
481+
482+
#ifdef CONFIG_HIBERNATION
483+
int hibernate_resume_nonboot_cpu_disable(void)
484+
{
485+
int ret;
486+
487+
play_dead = poll_play_dead;
488+
ret = suspend_disable_secondary_cpus();
489+
play_dead = idle_play_dead;
490+
491+
return ret;
492+
}
493+
#endif
494+
450495
#endif
451496

452497
/*

0 commit comments

Comments
 (0)