LoongArch re-enables interrupts on its idle routine and performs a
TIF_NEED_RESCHED check afterwards before putting the CPU to sleep.
The IRQs firing between the check and the idle instruction may set the
TIF_NEED_RESCHED flag. In order to deal with such a race, IRQs
interrupting __arch_cpu_idle() rollback their return address to the
beginning of __arch_cpu_idle() so that TIF_NEED_RESCHED is checked
again before going back to sleep.
However idle IRQs can also queue timers that may require a tick
reprogramming through a new generic idle loop iteration but those timers
would go unnoticed here because __arch_cpu_idle() only checks
TIF_NEED_RESCHED. It doesn't check for pending timers.
Fix this with fast-forwarding idle IRQs return address to the end of the
idle routine instead of the beginning, so that the generic idle loop can
handle both TIF_NEED_RESCHED and pending timers.
Fixes: 0603839b18
("LoongArch: Add exception/interrupt handling")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
107 lines
2.6 KiB
ArmAsm
107 lines
2.6 KiB
ArmAsm
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
|
*
|
|
* Derived from MIPS:
|
|
* Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle
|
|
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
|
|
* Copyright (C) 2002, 2007 Maciej W. Rozycki
|
|
* Copyright (C) 2001, 2012 MIPS Technologies, Inc. All rights reserved.
|
|
*/
|
|
#include <asm/asm.h>
|
|
#include <asm/asmmacro.h>
|
|
#include <asm/loongarch.h>
|
|
#include <asm/regdef.h>
|
|
#include <asm/fpregdef.h>
|
|
#include <asm/stackframe.h>
|
|
#include <asm/thread_info.h>
|
|
|
|
.align 5
|
|
SYM_FUNC_START(__arch_cpu_idle)
|
|
/* start of idle interrupt region */
|
|
ori t0, zero, CSR_CRMD_IE
|
|
/* idle instruction needs irq enabled */
|
|
csrxchg t0, t0, LOONGARCH_CSR_CRMD
|
|
/*
|
|
* If an interrupt lands here; between enabling interrupts above and
|
|
* going idle on the next instruction, we must *NOT* go idle since the
|
|
* interrupt could have set TIF_NEED_RESCHED or caused an timer to need
|
|
* reprogramming. Fall through -- see handle_vint() below -- and have
|
|
* the idle loop take care of things.
|
|
*/
|
|
idle 0
|
|
/* end of idle interrupt region */
|
|
1: jr ra
|
|
SYM_FUNC_END(__arch_cpu_idle)
|
|
|
|
SYM_CODE_START(handle_vint)
|
|
UNWIND_HINT_UNDEFINED
|
|
BACKUP_T0T1
|
|
SAVE_ALL
|
|
la_abs t1, 1b
|
|
LONG_L t0, sp, PT_ERA
|
|
/* 3 instructions idle interrupt region */
|
|
ori t0, t0, 0b1100
|
|
bne t0, t1, 1f
|
|
LONG_S t0, sp, PT_ERA
|
|
1: move a0, sp
|
|
move a1, sp
|
|
la_abs t0, do_vint
|
|
jirl ra, t0, 0
|
|
RESTORE_ALL_AND_RET
|
|
SYM_CODE_END(handle_vint)
|
|
|
|
SYM_CODE_START(except_vec_cex)
|
|
UNWIND_HINT_UNDEFINED
|
|
b cache_parity_error
|
|
SYM_CODE_END(except_vec_cex)
|
|
|
|
.macro build_prep_badv
|
|
csrrd t0, LOONGARCH_CSR_BADV
|
|
PTR_S t0, sp, PT_BVADDR
|
|
.endm
|
|
|
|
.macro build_prep_fcsr
|
|
movfcsr2gr a1, fcsr0
|
|
.endm
|
|
|
|
.macro build_prep_none
|
|
.endm
|
|
|
|
.macro BUILD_HANDLER exception handler prep
|
|
.align 5
|
|
SYM_CODE_START(handle_\exception)
|
|
UNWIND_HINT_UNDEFINED
|
|
666:
|
|
BACKUP_T0T1
|
|
SAVE_ALL
|
|
build_prep_\prep
|
|
move a0, sp
|
|
la_abs t0, do_\handler
|
|
jirl ra, t0, 0
|
|
668:
|
|
RESTORE_ALL_AND_RET
|
|
SYM_CODE_END(handle_\exception)
|
|
.pushsection ".data", "aw", %progbits
|
|
SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
|
|
.popsection
|
|
.endm
|
|
|
|
BUILD_HANDLER ade ade badv
|
|
BUILD_HANDLER ale ale badv
|
|
BUILD_HANDLER bce bce none
|
|
BUILD_HANDLER bp bp none
|
|
BUILD_HANDLER fpe fpe fcsr
|
|
BUILD_HANDLER fpu fpu none
|
|
BUILD_HANDLER lsx lsx none
|
|
BUILD_HANDLER lasx lasx none
|
|
BUILD_HANDLER lbt lbt none
|
|
BUILD_HANDLER ri ri none
|
|
BUILD_HANDLER watch watch none
|
|
BUILD_HANDLER reserved reserved none /* others */
|
|
|
|
SYM_CODE_START(handle_sys)
|
|
UNWIND_HINT_UNDEFINED
|
|
la_abs t0, handle_syscall
|
|
jr t0
|
|
SYM_CODE_END(handle_sys)
|