162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NMI backtrace support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Gratuitously copied from arch/x86/kernel/apic/hw_nmi.c by Russell King, 662306a36Sopenharmony_ci * with the following header: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * HW NMI watchdog support 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * started by Don Zickus, Copyright (C) 2010 Red Hat, Inc. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Arch specific calls to support NMI watchdog 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Bits copied from original nmi.c file 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#include <linux/cpumask.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/kprobes.h> 1962306a36Sopenharmony_ci#include <linux/nmi.h> 2062306a36Sopenharmony_ci#include <linux/cpu.h> 2162306a36Sopenharmony_ci#include <linux/sched/debug.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifdef arch_trigger_cpumask_backtrace 2462306a36Sopenharmony_ci/* For reliability, we're prepared to waste bits here. */ 2562306a36Sopenharmony_cistatic DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* "in progress" flag of arch_trigger_cpumask_backtrace */ 2862306a36Sopenharmony_cistatic unsigned long backtrace_flag; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * When raise() is called it will be passed a pointer to the 3262306a36Sopenharmony_ci * backtrace_mask. Architectures that call nmi_cpu_backtrace() 3362306a36Sopenharmony_ci * directly from their raise() functions may rely on the mask 3462306a36Sopenharmony_ci * they are passed being updated as a side effect of this call. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_civoid nmi_trigger_cpumask_backtrace(const cpumask_t *mask, 3762306a36Sopenharmony_ci int exclude_cpu, 3862306a36Sopenharmony_ci void (*raise)(cpumask_t *mask)) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int i, this_cpu = get_cpu(); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (test_and_set_bit(0, &backtrace_flag)) { 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * If there is already a trigger_all_cpu_backtrace() in progress 4562306a36Sopenharmony_ci * (backtrace_flag == 1), don't output double cpu dump infos. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci put_cpu(); 4862306a36Sopenharmony_ci return; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci cpumask_copy(to_cpumask(backtrace_mask), mask); 5262306a36Sopenharmony_ci if (exclude_cpu != -1) 5362306a36Sopenharmony_ci cpumask_clear_cpu(exclude_cpu, to_cpumask(backtrace_mask)); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* 5662306a36Sopenharmony_ci * Don't try to send an NMI to this cpu; it may work on some 5762306a36Sopenharmony_ci * architectures, but on others it may not, and we'll get 5862306a36Sopenharmony_ci * information at least as useful just by doing a dump_stack() here. 5962306a36Sopenharmony_ci * Note that nmi_cpu_backtrace(NULL) will clear the cpu bit. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci if (cpumask_test_cpu(this_cpu, to_cpumask(backtrace_mask))) 6262306a36Sopenharmony_ci nmi_cpu_backtrace(NULL); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!cpumask_empty(to_cpumask(backtrace_mask))) { 6562306a36Sopenharmony_ci pr_info("Sending NMI from CPU %d to CPUs %*pbl:\n", 6662306a36Sopenharmony_ci this_cpu, nr_cpumask_bits, to_cpumask(backtrace_mask)); 6762306a36Sopenharmony_ci nmi_backtrace_stall_snap(to_cpumask(backtrace_mask)); 6862306a36Sopenharmony_ci raise(to_cpumask(backtrace_mask)); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Wait for up to 10 seconds for all CPUs to do the backtrace */ 7262306a36Sopenharmony_ci for (i = 0; i < 10 * 1000; i++) { 7362306a36Sopenharmony_ci if (cpumask_empty(to_cpumask(backtrace_mask))) 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci mdelay(1); 7662306a36Sopenharmony_ci touch_softlockup_watchdog(); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci nmi_backtrace_stall_check(to_cpumask(backtrace_mask)); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * Force flush any remote buffers that might be stuck in IRQ context 8262306a36Sopenharmony_ci * and therefore could not run their irq_work. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci printk_trigger_flush(); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci clear_bit_unlock(0, &backtrace_flag); 8762306a36Sopenharmony_ci put_cpu(); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci// Dump stacks even for idle CPUs. 9162306a36Sopenharmony_cistatic bool backtrace_idle; 9262306a36Sopenharmony_cimodule_param(backtrace_idle, bool, 0644); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cibool nmi_cpu_backtrace(struct pt_regs *regs) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int cpu = smp_processor_id(); 9762306a36Sopenharmony_ci unsigned long flags; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * Allow nested NMI backtraces while serializing 10262306a36Sopenharmony_ci * against other CPUs. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci printk_cpu_sync_get_irqsave(flags); 10562306a36Sopenharmony_ci if (!READ_ONCE(backtrace_idle) && regs && cpu_in_idle(instruction_pointer(regs))) { 10662306a36Sopenharmony_ci pr_warn("NMI backtrace for cpu %d skipped: idling at %pS\n", 10762306a36Sopenharmony_ci cpu, (void *)instruction_pointer(regs)); 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci pr_warn("NMI backtrace for cpu %d\n", cpu); 11062306a36Sopenharmony_ci if (regs) 11162306a36Sopenharmony_ci show_regs(regs); 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci dump_stack(); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci printk_cpu_sync_put_irqrestore(flags); 11662306a36Sopenharmony_ci cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); 11762306a36Sopenharmony_ci return true; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return false; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ciNOKPROBE_SYMBOL(nmi_cpu_backtrace); 12362306a36Sopenharmony_ci#endif 124