Skip to content

Commit d8bb671

Browse files
mhiramatwilldeacon
authored andcommitted
arm64: Make debug exception handlers visible from RCU
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 hardkernel#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 hardkernel#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]>
1 parent b3980e4 commit d8bb671

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
@@ -777,6 +777,53 @@ void __init hook_debug_fault_code(int nr,
777777
debug_fault_info[nr].name = name;
778778
}
779779

780+
/*
781+
* In debug exception context, we explicitly disable preemption despite
782+
* having interrupts disabled.
783+
* This serves two purposes: it makes it much less likely that we would
784+
* accidentally schedule in exception context and it will force a warning
785+
* if we somehow manage to schedule by accident.
786+
*/
787+
static void debug_exception_enter(struct pt_regs *regs)
788+
{
789+
/*
790+
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
791+
* already disabled to preserve the last enabled/disabled addresses.
792+
*/
793+
if (interrupts_enabled(regs))
794+
trace_hardirqs_off();
795+
796+
if (user_mode(regs)) {
797+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
798+
} else {
799+
/*
800+
* We might have interrupted pretty much anything. In
801+
* fact, if we're a debug exception, we can even interrupt
802+
* NMI processing. We don't want this code makes in_nmi()
803+
* to return true, but we need to notify RCU.
804+
*/
805+
rcu_nmi_enter();
806+
}
807+
808+
preempt_disable();
809+
810+
/* This code is a bit fragile. Test it. */
811+
RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work");
812+
}
813+
NOKPROBE_SYMBOL(debug_exception_enter);
814+
815+
static void debug_exception_exit(struct pt_regs *regs)
816+
{
817+
preempt_enable_no_resched();
818+
819+
if (!user_mode(regs))
820+
rcu_nmi_exit();
821+
822+
if (interrupts_enabled(regs))
823+
trace_hardirqs_on();
824+
}
825+
NOKPROBE_SYMBOL(debug_exception_exit);
826+
780827
#ifdef CONFIG_ARM64_ERRATUM_1463225
781828
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
782829

@@ -817,12 +864,7 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
817864
if (cortex_a76_erratum_1463225_debug_handler(regs))
818865
return;
819866

820-
/*
821-
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
822-
* already disabled to preserve the last enabled/disabled addresses.
823-
*/
824-
if (interrupts_enabled(regs))
825-
trace_hardirqs_off();
867+
debug_exception_enter(regs);
826868

827869
if (user_mode(regs) && !is_ttbr0_addr(pc))
828870
arm64_apply_bp_hardening();
@@ -832,7 +874,6 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
832874
inf->sig, inf->code, (void __user *)pc, esr);
833875
}
834876

835-
if (interrupts_enabled(regs))
836-
trace_hardirqs_on();
877+
debug_exception_exit(regs);
837878
}
838879
NOKPROBE_SYMBOL(do_debug_exception);

0 commit comments

Comments
 (0)