Skip to content

Commit e058f41

Browse files
mhiramatSasha Levin
authored andcommitted
arm64: Make debug exception handlers visible from RCU
[ Upstream commit d8bb671 ] Make debug exceptions visible from RCU so that synchronize_rcu() correctly track the debug exception handler. This also introduces sanity checks for user-mode exceptions as same as x86's ist_enter()/ist_exit(). The debug exception can interrupt in idle task. For example, it warns if we put a kprobe on a function called from idle task as below. The warning message showed that the rcu_read_lock() caused this problem. But actually, this means the RCU is lost the context which is already in NMI/IRQ. /sys/kernel/debug/tracing # echo p default_idle_call >> kprobe_events /sys/kernel/debug/tracing # echo 1 > events/kprobes/enable /sys/kernel/debug/tracing # [ 135.122237] [ 135.125035] ============================= [ 135.125310] WARNING: suspicious RCU usage [ 135.125581] 5.2.0-08445-g9187c508bdc7 #20 Not tainted [ 135.125904] ----------------------------- [ 135.126205] include/linux/rcupdate.h:594 rcu_read_lock() used illegally while idle! [ 135.126839] [ 135.126839] other info that might help us debug this: [ 135.126839] [ 135.127410] [ 135.127410] RCU used illegally from idle CPU! [ 135.127410] rcu_scheduler_active = 2, debug_locks = 1 [ 135.128114] RCU used illegally from extended quiescent state! [ 135.128555] 1 lock held by swapper/0/0: [ 135.128944] #0: (____ptrval____) (rcu_read_lock){....}, at: call_break_hook+0x0/0x178 [ 135.130499] [ 135.130499] stack backtrace: [ 135.131192] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.2.0-08445-g9187c508bdc7 #20 [ 135.131841] Hardware name: linux,dummy-virt (DT) [ 135.132224] Call trace: [ 135.132491] dump_backtrace+0x0/0x140 [ 135.132806] show_stack+0x24/0x30 [ 135.133133] dump_stack+0xc4/0x10c [ 135.133726] lockdep_rcu_suspicious+0xf8/0x108 [ 135.134171] call_break_hook+0x170/0x178 [ 135.134486] brk_handler+0x28/0x68 [ 135.134792] do_debug_exception+0x90/0x150 [ 135.135051] el1_dbg+0x18/0x8c [ 135.135260] default_idle_call+0x0/0x44 [ 135.135516] cpu_startup_entry+0x2c/0x30 [ 135.135815] rest_init+0x1b0/0x280 [ 135.136044] arch_call_rest_init+0x14/0x1c [ 135.136305] start_kernel+0x4d4/0x500 [ 135.136597] So make debug exception visible to RCU can fix this warning. Reported-by: Naresh Kamboju <[email protected]> Acked-by: Paul E. McKenney <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent e38e847 commit e058f41

File tree

1 file changed

+49
-8
lines changed

1 file changed

+49
-8
lines changed

arch/arm64/mm/fault.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,53 @@ void __init hook_debug_fault_code(int nr,
800800
debug_fault_info[nr].name = name;
801801
}
802802

803+
/*
804+
* In debug exception context, we explicitly disable preemption despite
805+
* having interrupts disabled.
806+
* This serves two purposes: it makes it much less likely that we would
807+
* accidentally schedule in exception context and it will force a warning
808+
* if we somehow manage to schedule by accident.
809+
*/
810+
static void debug_exception_enter(struct pt_regs *regs)
811+
{
812+
/*
813+
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
814+
* already disabled to preserve the last enabled/disabled addresses.
815+
*/
816+
if (interrupts_enabled(regs))
817+
trace_hardirqs_off();
818+
819+
if (user_mode(regs)) {
820+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
821+
} else {
822+
/*
823+
* We might have interrupted pretty much anything. In
824+
* fact, if we're a debug exception, we can even interrupt
825+
* NMI processing. We don't want this code makes in_nmi()
826+
* to return true, but we need to notify RCU.
827+
*/
828+
rcu_nmi_enter();
829+
}
830+
831+
preempt_disable();
832+
833+
/* This code is a bit fragile. Test it. */
834+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work");
835+
}
836+
NOKPROBE_SYMBOL(debug_exception_enter);
837+
838+
static void debug_exception_exit(struct pt_regs *regs)
839+
{
840+
preempt_enable_no_resched();
841+
842+
if (!user_mode(regs))
843+
rcu_nmi_exit();
844+
845+
if (interrupts_enabled(regs))
846+
trace_hardirqs_on();
847+
}
848+
NOKPROBE_SYMBOL(debug_exception_exit);
849+
803850
#ifdef CONFIG_ARM64_ERRATUM_1463225
804851
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
805852

@@ -840,12 +887,7 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
840887
if (cortex_a76_erratum_1463225_debug_handler(regs))
841888
return;
842889

843-
/*
844-
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
845-
* already disabled to preserve the last enabled/disabled addresses.
846-
*/
847-
if (interrupts_enabled(regs))
848-
trace_hardirqs_off();
890+
debug_exception_enter(regs);
849891

850892
if (user_mode(regs) && !is_ttbr0_addr(pc))
851893
arm64_apply_bp_hardening();
@@ -855,7 +897,6 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
855897
inf->sig, inf->code, (void __user *)pc, esr);
856898
}
857899

858-
if (interrupts_enabled(regs))
859-
trace_hardirqs_on();
900+
debug_exception_exit(regs);
860901
}
861902
NOKPROBE_SYMBOL(do_debug_exception);

0 commit comments

Comments
 (0)