mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
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 #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 <naresh.kamboju@linaro.org> Acked-by: Paul E. McKenney <paulmck@linux.ibm.com> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
b3980e4852
commit
d8bb6718c4
@ -777,6 +777,53 @@ void __init hook_debug_fault_code(int nr,
|
|||||||
debug_fault_info[nr].name = name;
|
debug_fault_info[nr].name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In debug exception context, we explicitly disable preemption despite
|
||||||
|
* having interrupts disabled.
|
||||||
|
* This serves two purposes: it makes it much less likely that we would
|
||||||
|
* accidentally schedule in exception context and it will force a warning
|
||||||
|
* if we somehow manage to schedule by accident.
|
||||||
|
*/
|
||||||
|
static void debug_exception_enter(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
|
||||||
|
* already disabled to preserve the last enabled/disabled addresses.
|
||||||
|
*/
|
||||||
|
if (interrupts_enabled(regs))
|
||||||
|
trace_hardirqs_off();
|
||||||
|
|
||||||
|
if (user_mode(regs)) {
|
||||||
|
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We might have interrupted pretty much anything. In
|
||||||
|
* fact, if we're a debug exception, we can even interrupt
|
||||||
|
* NMI processing. We don't want this code makes in_nmi()
|
||||||
|
* to return true, but we need to notify RCU.
|
||||||
|
*/
|
||||||
|
rcu_nmi_enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
|
||||||
|
/* This code is a bit fragile. Test it. */
|
||||||
|
RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work");
|
||||||
|
}
|
||||||
|
NOKPROBE_SYMBOL(debug_exception_enter);
|
||||||
|
|
||||||
|
static void debug_exception_exit(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
preempt_enable_no_resched();
|
||||||
|
|
||||||
|
if (!user_mode(regs))
|
||||||
|
rcu_nmi_exit();
|
||||||
|
|
||||||
|
if (interrupts_enabled(regs))
|
||||||
|
trace_hardirqs_on();
|
||||||
|
}
|
||||||
|
NOKPROBE_SYMBOL(debug_exception_exit);
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_ERRATUM_1463225
|
#ifdef CONFIG_ARM64_ERRATUM_1463225
|
||||||
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
|
DECLARE_PER_CPU(int, __in_cortex_a76_erratum_1463225_wa);
|
||||||
|
|
||||||
@ -817,12 +864,7 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
|
|||||||
if (cortex_a76_erratum_1463225_debug_handler(regs))
|
if (cortex_a76_erratum_1463225_debug_handler(regs))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
debug_exception_enter(regs);
|
||||||
* Tell lockdep we disabled irqs in entry.S. Do nothing if they were
|
|
||||||
* already disabled to preserve the last enabled/disabled addresses.
|
|
||||||
*/
|
|
||||||
if (interrupts_enabled(regs))
|
|
||||||
trace_hardirqs_off();
|
|
||||||
|
|
||||||
if (user_mode(regs) && !is_ttbr0_addr(pc))
|
if (user_mode(regs) && !is_ttbr0_addr(pc))
|
||||||
arm64_apply_bp_hardening();
|
arm64_apply_bp_hardening();
|
||||||
@ -832,7 +874,6 @@ asmlinkage void __exception do_debug_exception(unsigned long addr_if_watchpoint,
|
|||||||
inf->sig, inf->code, (void __user *)pc, esr);
|
inf->sig, inf->code, (void __user *)pc, esr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interrupts_enabled(regs))
|
debug_exception_exit(regs);
|
||||||
trace_hardirqs_on();
|
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(do_debug_exception);
|
NOKPROBE_SYMBOL(do_debug_exception);
|
||||||
|
Loading…
Reference in New Issue
Block a user