From a720e3676c1ec4fd79b0636379964833e52b22fa Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Sun, 17 Nov 2019 18:01:10 +0100 Subject: [PATCH] k1c: Add LOCKDEP support Summary: In order to enable lockdep support, we need a compelte TRACE_IRQFLAGS support. While enabled previously, there no trace_hardirqs support leading to various errors when enabling PROVE_LOCKING. This patch adds necessary calls to trace_hardirqs_on/off in assembly to correctly track irqs state when entering exceptions. During testing, a problem arose when calling trace_hardirqs_off indirectly from module: ``` ------------[ cut here ]------------ WARNING: CPU: 0 PID: 59 at kernel/locking/lockdep.c:4329 check_flags.part.23+0x23c/0x240 DEBUG_LOCKS_WARN_ON(current->hardirqs_enabled) Modules linked in: crc32c_generic(+) loop ext4 crc16 mbcache jbd2 crypto_hash crypto_algapi crypto m) CPU: 0 PID: 59 Comm: modprobe Tainted: G O 5.3.0 #135 Hardware name: Kalray HAPS prototyping (LS) (DT) ... Call Trace: [] dump_stack+0x30/0x50 [] __warn+0x138/0x158 [] warn_slowpath_fmt+0x58/0x78 [] check_flags.part.23+0x23c/0x240 [] lock_is_held_type+0x1a4/0x1b0 [] rcu_read_lock_sched_held+0x88/0x90 [] module_assert_mutex_or_preempt+0x2c/0x98 [] __module_address+0x48/0x150 [] __module_text_address+0x20/0xb0 [] is_module_text_address+0x18/0x38 [] kernel_text_address+0x8c/0xb8 [] __kernel_text_address+0x20/0x90 [] walk_stackframe+0x78/0xe0 [] return_address+0x58/0x90 [] trace_hardirqs_off+0x68/0x1b8 [] kfree+0xe4/0x328 [] crypto_larval_destroy+0x54/0x78 [crypto] [] crypto_larval_kill+0xd4/0xf8 [crypto] [] crypto_wait_for_test+0x9c/0x108 [crypto_algapi] [] crypto_register_alg+0xdc/0xe8 [crypto_algapi] [] crypto_register_shash+0x48/0x70 [crypto_hash] [] crc32c_mod_init+0x30/0x4c [crc32c_generic] [] do_one_initcall+0x70/0x2b0 [] do_init_module+0x74/0x270 [] load_module+0x223c/0x26f0 [] sys_finit_module+0xb0/0x118 irq event stamp: 1989 hardirqs last enabled at (1989): [] _raw_spin_unlock_irqrestore+0x8c/0xa0 hardirqs last disabled at (1988): [] _raw_spin_lock_irqsave+0x34/0x88 softirqs last enabled at (1940): [] _etext+0x38c/0x2340 softirqs last disabled at (1935): [] irq_exit+0xa4/0xa8 ---[ end trace f667df957dc78eed ]--- possible reason: unannotated irqs-off. ``` Problem is due to the fact trace_hardirqs_off used CALLER_ADDR0 and CALLER_ADDR1. These macros indirectly uses return_addr. Then in walk_stackframe, address of frame pointer ra is checked to be a kernel address using __kernel_text_address. In this function, __module_address take a lock which trigger a hardirqs check that fails. However, since we are calling it to report that trace_hardirqs are off, this is triggering a check before even tracking that irqs are off. Remove the call to __kernel_text_address as it is also not done on arm64. Ref T10401 Test Plan: Executed both smp & valid images on FPGA and verified that there are no signaled lockdep errors. Note that this can't run on debug image since the overhead to call trace_hardirqs_on in exception does not leave any time for normal code to execute. Reviewers: O51 Linux Coolidge, gthouvenin Reviewed By: O51 Linux Coolidge, gthouvenin Subscribers: gthouvenin, jcpince, alfred, #linux_coolidge_cc Maniphest Tasks: T10401 Differential Revision: https://phab.kalray.eu/D2034 --- arch/k1c/Kconfig | 3 +++ arch/k1c/kernel/entry.S | 37 +++++++++++++++++++++++++++++++++++- arch/k1c/kernel/signal.c | 4 ++++ arch/k1c/kernel/smpboot.c | 1 + arch/k1c/kernel/stacktrace.c | 3 --- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/arch/k1c/Kconfig b/arch/k1c/Kconfig index 4d3b7c2f0f95a9..eb8ed18856fdd1 100644 --- a/arch/k1c/Kconfig +++ b/arch/k1c/Kconfig @@ -39,6 +39,9 @@ config ZONE_DMA32 config STACKTRACE_SUPPORT def_bool y +config LOCKDEP_SUPPORT + def_bool y + config TRACE_IRQFLAGS_SUPPORT def_bool y diff --git a/arch/k1c/kernel/entry.S b/arch/k1c/kernel/entry.S index 9de56de3d0da0b..d3bef751fded8a 100644 --- a/arch/k1c/kernel/entry.S +++ b/arch/k1c/kernel/entry.S @@ -64,6 +64,17 @@ asn_error_panic_str_label: .string "ASN mismatch !" #endif +/** + * call_trace_hardirqs: hardirqs tracing call + * state: State of hardirqs to be reported (on/off) + */ +.macro call_trace_hardirqs state +#ifdef CONFIG_TRACE_IRQFLAGS + call trace_hardirqs_\state + ;; +#endif +.endm + /** * disable_interrupts: Disable interrupts * tmp_reg: Temporary register to use for interrupt disabling @@ -79,6 +90,7 @@ asn_error_panic_str_label: * call_do_work_pending: Call do_work_pending and set stack argument ($r0) * NOTE: Since do_work_pending requires thread_flags in $r1, they must * be provided in $r1 before calling this macro. + * Moreover, do_work_pending expects interrupts to be disabled. */ .macro call_do_work_pending copyd $r0 = $sp @@ -221,10 +233,15 @@ asn_error_panic_str_label: make $fp = 0 /* Reenable hwloop and clear exception taken */ make $r8 = PS_HWLOOP_EN_ET_EN - copyd \pt_regs_sp = $sp ;; wfxl $ps, $r8 ;; + /* When entering exceptions, IRQs are always disabled */ + call_trace_hardirqs off + ;; + /* Copy regs stack pointer for macro caller */ + copyd \pt_regs_sp = $sp + ;; #ifdef CONFIG_DEBUG_EXCEPTION_STACK addd $sp = $sp, -REG_SIZE ;; @@ -289,7 +306,22 @@ _check_ok: ;; call_do_work_pending ;; +#ifdef CONFIG_TRACE_IRQFLAGS + /* reload sps value from saved registers */ + ld $r6 = PT_SPS[$sp] + ;; +#endif _restore_regs: +#ifdef CONFIG_TRACE_IRQFLAGS + /* Check if IRQs are going to be reenable in next context */ + andd $r6 = $r6, K1C_SFR_SPS_IE_MASK + ;; + cb.deqz $r6? 1f + ;; + call trace_hardirqs_on + ;; +1: +#endif lo $r0r1r2r3 = PT_CS_SPC_SPS_ES[$sp] ;; lo $r4r5r6r7 = PT_LC_LE_LS_RA[$sp] @@ -1136,6 +1168,9 @@ call_work_pending: ;; call_do_work_pending ;; + /* Since we are returning to user, interrupts will be reenabled */ + call_trace_hardirqs on + ;; goto ret_to_user ;; diff --git a/arch/k1c/kernel/signal.c b/arch/k1c/kernel/signal.c index 433f3f49cde6bb..5b2dff46efff03 100644 --- a/arch/k1c/kernel/signal.c +++ b/arch/k1c/kernel/signal.c @@ -242,9 +242,13 @@ asmlinkage void do_signal(struct pt_regs *regs) restore_saved_sigmask(); } + asmlinkage void do_work_pending(struct pt_regs *regs, unsigned long thread_flags) { + /* We are called with IRQs disabled */ + trace_hardirqs_off(); + do { if (thread_flags & _TIF_NEED_RESCHED) { schedule(); diff --git a/arch/k1c/kernel/smpboot.c b/arch/k1c/kernel/smpboot.c index 9edd0715079848..7de5afa7849627 100644 --- a/arch/k1c/kernel/smpboot.c +++ b/arch/k1c/kernel/smpboot.c @@ -114,6 +114,7 @@ void __init start_kernel_secondary(void) notify_cpu_starting(cpu); set_cpu_online(cpu, true); + trace_hardirqs_off(); local_flush_tlb_all(); diff --git a/arch/k1c/kernel/stacktrace.c b/arch/k1c/kernel/stacktrace.c index 6a07ca14be013e..0edf6a39587baf 100644 --- a/arch/k1c/kernel/stacktrace.c +++ b/arch/k1c/kernel/stacktrace.c @@ -55,9 +55,6 @@ void notrace walk_stackframe(struct task_struct *task, struct stackframe *frame, while (1) { addr = frame->ra; - if (unlikely(!__kernel_text_address(addr))) - break; - if (fn(addr, arg)) break;