From mboxrd@z Thu Jan 1 00:00:00 1970 From: takahiro.akashi@linaro.org (AKASHI Takahiro) Date: Fri, 23 Sep 2016 16:33:26 +0900 Subject: [PATCH v2 2/3] arm64: kgdb: prevent kgdb from being invoked recursively In-Reply-To: <20160923073327.9657-1-takahiro.akashi@linaro.org> References: <20160923073327.9657-1-takahiro.akashi@linaro.org> Message-ID: <20160923073327.9657-3-takahiro.akashi@linaro.org> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org If a breakpoint is set in an interrupt-sensitive place, like gic_handle_irq(), a debug exception can be triggerred recursively. We will see the following message: KGDB: re-enter error: breakpoint removed ffffffc000081258 ------------[ cut here ]------------ WARNING: CPU: 0 PID: 650 at kernel/debug/debug_core.c:435 kgdb_handle_exception+0x1dc/0x1f4() Modules linked in: CPU: 0 PID: 650 Comm: sh Not tainted 3.17.0-rc2+ #177 Call trace: [] dump_backtrace+0x0/0x130 [] show_stack+0x10/0x1c [] dump_stack+0x74/0xb8 [] warn_slowpath_common+0x8c/0xb4 [] warn_slowpath_null+0x14/0x20 [] kgdb_handle_exception+0x1d8/0x1f4 [] kgdb_brk_fn+0x18/0x28 [] brk_handler+0x9c/0xe8 [] do_debug_exception+0x3c/0xac Exception stack(0xffffffc07e027650 to 0xffffffc07e027770) ... [] el1_dbg+0x14/0x68 [] kgdb_cpu_enter+0x464/0x5c0 [] kgdb_handle_exception+0x190/0x1f4 [] kgdb_brk_fn+0x18/0x28 [] brk_handler+0x9c/0xe8 [] do_debug_exception+0x3c/0xac Exception stack(0xffffffc07e027ac0 to 0xffffffc07e027be0) ... [] el1_dbg+0x14/0x68 [] __handle_sysrq+0x11c/0x190 [] write_sysrq_trigger+0x4c/0x60 [] proc_reg_write+0x54/0x84 [] vfs_write+0x98/0x1c8 [] SyS_write+0x40/0xa0 When some interrupt occurs, a breakpoint at gic_handle_irq() invokes kgdb. Kgdb then calls kgdb_roundup_cpus() to sync with other cpus. Current kgdb_roundup_cpus() unmasks interrupts temporarily to use smp_call_function(). This eventually allows another interrupt to occur and likely results in hitting a breakpoint at gic_handle_irq() again since a debug exception is always enabled in el1_irq. We can avoid this issue by specifying "nokgdbroundup" in a kernel command line, but this will also leave other cpus be in unknown state in terms of kgdb, and may result in interfering with kgdb. This patch re-implements kgdb_roundup_cpus() so that we won't have to enable interrupts there by using irq_work. Signed-off-by: AKASHI Takahiro Cc: Catalin Marinas Cc: Will Deacon Cc: Jason Wessel Cc: # 3.15- --- arch/arm64/kernel/kgdb.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c index afe5f90..59c4aec 100644 --- a/arch/arm64/kernel/kgdb.c +++ b/arch/arm64/kernel/kgdb.c @@ -19,10 +19,13 @@ * along with this program. If not, see . */ +#include #include +#include #include #include #include +#include #include struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { @@ -106,6 +109,8 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { { "fpcr", 4, -1 }, }; +static DEFINE_PER_CPU(struct irq_work, kgdb_irq_work); + char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) { if (regno >= DBG_MAX_REG_NUM || regno < 0) @@ -258,16 +263,27 @@ static struct step_hook kgdb_step_hook = { .fn = kgdb_step_brk_fn }; -static void kgdb_call_nmi_hook(void *ignored) +static void kgdb_roundup_hook(struct irq_work *work) { kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); } void kgdb_roundup_cpus(unsigned long flags) { - local_irq_enable(); - smp_call_function(kgdb_call_nmi_hook, NULL, 0); - local_irq_disable(); + int cpu; + struct cpumask mask; + struct irq_work *work; + + mask = *cpu_online_mask; + cpumask_clear_cpu(smp_processor_id(), &mask); + cpu = cpumask_first(&mask); + if (cpu >= nr_cpu_ids) + return; + + for_each_cpu(cpu, &mask) { + work = per_cpu_ptr(&kgdb_irq_work, cpu); + irq_work_queue_on(work, cpu); + } } static int __kgdb_notify(struct die_args *args, unsigned long cmd) @@ -308,6 +324,8 @@ static struct notifier_block kgdb_notifier = { int kgdb_arch_init(void) { int ret = register_die_notifier(&kgdb_notifier); + int cpu; + struct irq_work *work; if (ret != 0) return ret; @@ -315,6 +333,12 @@ int kgdb_arch_init(void) register_break_hook(&kgdb_brkpt_hook); register_break_hook(&kgdb_compiled_brkpt_hook); register_step_hook(&kgdb_step_hook); + + for_each_possible_cpu(cpu) { + work = per_cpu_ptr(&kgdb_irq_work, cpu); + init_irq_work(work, kgdb_roundup_hook); + } + return 0; } -- 2.10.0