Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tile: support delivering NMIs for multicore backtrace

A new hypervisor service was added some time ago (MDE 4.2.1 or
later, or MDE 4.3 or later) that allows cores to request NMIs
to be delivered to other cores. Use this facility to deliver
a request that causes a backtrace to be generated on each core,
and hook it into the magic SysRq functionality.

Signed-off-by: Chris Metcalf <cmetcalf@ezchip.com>

+197 -2
+5
arch/tile/include/asm/irq.h
··· 78 78 79 79 void setup_irq_regs(void); 80 80 81 + #ifdef __tilegx__ 82 + void arch_trigger_all_cpu_backtrace(bool self); 83 + #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace 84 + #endif 85 + 81 86 #endif /* _ASM_TILE_IRQ_H */
+8
arch/tile/include/asm/traps.h
··· 52 52 /* kernel/messaging.c */ 53 53 void hv_message_intr(struct pt_regs *, int intnum); 54 54 55 + #define TILE_NMI_DUMP_STACK 1 /* Dump stack for sysrq+'l' */ 56 + 57 + /* kernel/process.c */ 58 + void do_nmi_dump_stack(struct pt_regs *regs); 59 + 60 + /* kernel/traps.c */ 61 + void do_nmi(struct pt_regs *, int fault_num, unsigned long reason); 62 + 55 63 /* kernel/irq.c */ 56 64 void tile_dev_intr(struct pt_regs *, int intnum); 57 65
+59 -1
arch/tile/include/hv/hypervisor.h
··· 321 321 /** hv_console_set_ipi */ 322 322 #define HV_DISPATCH_CONSOLE_SET_IPI 63 323 323 324 + /** hv_send_nmi */ 325 + #define HV_DISPATCH_SEND_NMI 65 326 + 324 327 /** One more than the largest dispatch value */ 325 - #define _HV_DISPATCH_END 64 328 + #define _HV_DISPATCH_END 66 326 329 327 330 328 331 #ifndef __ASSEMBLER__ ··· 1256 1253 #define INT_DMATLB_ACCESS_DWNCL INT_DMA_CPL 1257 1254 /** Device interrupt downcall interrupt vector */ 1258 1255 #define INT_DEV_INTR_DWNCL INT_WORLD_ACCESS 1256 + /** NMI downcall interrupt vector */ 1257 + #define INT_NMI_DWNCL 64 1258 + 1259 + #define HV_NMI_FLAG_FORCE 0x1 /**< Force an NMI downcall regardless of 1260 + the ICS bit of the client. */ 1259 1261 1260 1262 #ifndef __ASSEMBLER__ 1261 1263 ··· 1786 1778 * error code. 1787 1779 */ 1788 1780 int hv_dev_poll_cancel(int devhdl); 1781 + 1782 + 1783 + /** NMI information */ 1784 + typedef struct 1785 + { 1786 + /** Result: negative error, or HV_NMI_RESULT_xxx. */ 1787 + int result; 1788 + 1789 + /** PC from interrupted remote core (if result != HV_NMI_RESULT_FAIL_HV). */ 1790 + HV_VirtAddr pc; 1791 + 1792 + } HV_NMI_Info; 1793 + 1794 + /** NMI issued successfully. */ 1795 + #define HV_NMI_RESULT_OK 0 1796 + 1797 + /** NMI not issued: remote tile running at client PL with ICS set. */ 1798 + #define HV_NMI_RESULT_FAIL_ICS 1 1799 + 1800 + /** NMI not issued: remote tile waiting in hypervisor. */ 1801 + #define HV_NMI_RESULT_FAIL_HV 2 1802 + 1803 + /** Force an NMI downcall regardless of the ICS bit of the client. */ 1804 + #define HV_NMI_FLAG_FORCE 0x1 1805 + 1806 + /** Send an NMI interrupt request to a particular tile. 1807 + * 1808 + * This will cause the NMI to be issued on the remote tile regardless 1809 + * of the state of the client interrupt mask. However, if the remote 1810 + * tile is in the hypervisor, it will not execute the NMI, and 1811 + * HV_NMI_RESULT_FAIL_HV will be returned. Similarly, if the remote 1812 + * tile is in a client interrupt critical section at the time of the 1813 + * NMI, it will not execute the NMI, and HV_NMI_RESULT_FAIL_ICS will 1814 + * be returned. In this second case, however, if HV_NMI_FLAG_FORCE 1815 + * is set in flags, then the remote tile will enter its NMI interrupt 1816 + * vector regardless. Forcing the NMI vector during an interrupt 1817 + * critical section will mean that the client can not safely continue 1818 + * execution after handling the interrupt. 1819 + * 1820 + * @param tile Tile to which the NMI request is sent. 1821 + * @param info NMI information which is defined by and interpreted by the 1822 + * supervisor, is passed to the specified tile, and is 1823 + * stored in the SPR register SYSTEM_SAVE_{CLIENT_PL}_2 on the 1824 + * specified tile when entering the NMI handler routine. 1825 + * Typically, this parameter stores the NMI type, or an aligned 1826 + * VA plus some special bits, etc. 1827 + * @param flags Flags (HV_NMI_FLAG_xxx). 1828 + * @return Information about the requested NMI. 1829 + */ 1830 + HV_NMI_Info hv_send_nmi(HV_Coord tile, unsigned long info, __hv64 flags); 1789 1831 1790 1832 1791 1833 /** Scatter-gather list for preada/pwritea calls. */
+2 -1
arch/tile/kernel/hvglue.S
··· 71 71 gensym hv_get_ipi_pte, 0x700, 32 72 72 gensym hv_set_pte_super_shift, 0x720, 32 73 73 gensym hv_console_set_ipi, 0x7e0, 32 74 - gensym hv_glue_internals, 0x800, 30720 74 + gensym hv_send_nmi, 0x820, 32 75 + gensym hv_glue_internals, 0x820, 30688
+4
arch/tile/kernel/hvglue_trace.c
··· 75 75 #define hv_get_ipi_pte _hv_get_ipi_pte 76 76 #define hv_set_pte_super_shift _hv_set_pte_super_shift 77 77 #define hv_console_set_ipi _hv_console_set_ipi 78 + #define hv_send_nmi _hv_send_nmi 78 79 #include <hv/hypervisor.h> 79 80 #undef hv_init 80 81 #undef hv_install_context ··· 135 134 #undef hv_get_ipi_pte 136 135 #undef hv_set_pte_super_shift 137 136 #undef hv_console_set_ipi 137 + #undef hv_send_nmi 138 138 139 139 /* 140 140 * Provide macros based on <linux/syscalls.h> to provide a wrapper ··· 266 264 HV_VirtAddr, tlb_va, unsigned long, tlb_length, 267 265 unsigned long, tlb_pgsize, unsigned long*, tlb_cpumask, 268 266 HV_Remote_ASID*, asids, int, asidcount) 267 + HV_WRAP3(HV_NMI_Info, hv_send_nmi, HV_Coord, tile, unsigned long, info, 268 + __hv64, flags)
+6
arch/tile/kernel/intvec_64.S
··· 515 515 .ifc \c_routine, handle_perf_interrupt 516 516 mfspr r2, AUX_PERF_COUNT_STS 517 517 .endif 518 + .ifc \c_routine, do_nmi 519 + mfspr r2, SPR_SYSTEM_SAVE_K_2 /* nmi type */ 520 + .else 521 + .endif 518 522 .endif 519 523 .endif 520 524 .endif ··· 1575 1571 1576 1572 /* Synthetic interrupt delivered only by the simulator */ 1577 1573 int_hand INT_BREAKPOINT, BREAKPOINT, do_breakpoint 1574 + /* Synthetic interrupt delivered by hv */ 1575 + int_hand INT_NMI_DWNCL, NMI_DWNCL, do_nmi, handle_nmi
+101
arch/tile/kernel/process.c
··· 27 27 #include <linux/kernel.h> 28 28 #include <linux/tracehook.h> 29 29 #include <linux/signal.h> 30 + #include <linux/delay.h> 30 31 #include <linux/context_tracking.h> 31 32 #include <asm/stack.h> 32 33 #include <asm/switch_to.h> ··· 575 574 576 575 dump_stack_regs(regs); 577 576 } 577 + 578 + /* To ensure stack dump on tiles occurs one by one. */ 579 + static DEFINE_SPINLOCK(backtrace_lock); 580 + /* To ensure no backtrace occurs before all of the stack dump are done. */ 581 + static atomic_t backtrace_cpus; 582 + /* The cpu mask to avoid reentrance. */ 583 + static struct cpumask backtrace_mask; 584 + 585 + void do_nmi_dump_stack(struct pt_regs *regs) 586 + { 587 + int is_idle = is_idle_task(current) && !in_interrupt(); 588 + int cpu; 589 + 590 + nmi_enter(); 591 + cpu = smp_processor_id(); 592 + if (WARN_ON_ONCE(!cpumask_test_and_clear_cpu(cpu, &backtrace_mask))) 593 + goto done; 594 + 595 + spin_lock(&backtrace_lock); 596 + if (is_idle) 597 + pr_info("CPU: %d idle\n", cpu); 598 + else 599 + show_regs(regs); 600 + spin_unlock(&backtrace_lock); 601 + atomic_dec(&backtrace_cpus); 602 + done: 603 + nmi_exit(); 604 + } 605 + 606 + #ifdef __tilegx__ 607 + void arch_trigger_all_cpu_backtrace(bool self) 608 + { 609 + struct cpumask mask; 610 + HV_Coord tile; 611 + unsigned int timeout; 612 + int cpu; 613 + int ongoing; 614 + HV_NMI_Info info[NR_CPUS]; 615 + 616 + ongoing = atomic_cmpxchg(&backtrace_cpus, 0, num_online_cpus() - 1); 617 + if (ongoing != 0) { 618 + pr_err("Trying to do all-cpu backtrace.\n"); 619 + pr_err("But another all-cpu backtrace is ongoing (%d cpus left)\n", 620 + ongoing); 621 + if (self) { 622 + pr_err("Reporting the stack on this cpu only.\n"); 623 + dump_stack(); 624 + } 625 + return; 626 + } 627 + 628 + cpumask_copy(&mask, cpu_online_mask); 629 + cpumask_clear_cpu(smp_processor_id(), &mask); 630 + cpumask_copy(&backtrace_mask, &mask); 631 + 632 + /* Backtrace for myself first. */ 633 + if (self) 634 + dump_stack(); 635 + 636 + /* Tentatively dump stack on remote tiles via NMI. */ 637 + timeout = 100; 638 + while (!cpumask_empty(&mask) && timeout) { 639 + for_each_cpu(cpu, &mask) { 640 + tile.x = cpu_x(cpu); 641 + tile.y = cpu_y(cpu); 642 + info[cpu] = hv_send_nmi(tile, TILE_NMI_DUMP_STACK, 0); 643 + if (info[cpu].result == HV_NMI_RESULT_OK) 644 + cpumask_clear_cpu(cpu, &mask); 645 + } 646 + 647 + mdelay(10); 648 + timeout--; 649 + } 650 + 651 + /* Warn about cpus stuck in ICS and decrement their counts here. */ 652 + if (!cpumask_empty(&mask)) { 653 + for_each_cpu(cpu, &mask) { 654 + switch (info[cpu].result) { 655 + case HV_NMI_RESULT_FAIL_ICS: 656 + pr_warn("Skipping stack dump of cpu %d in ICS at pc %#llx\n", 657 + cpu, info[cpu].pc); 658 + break; 659 + case HV_NMI_RESULT_FAIL_HV: 660 + pr_warn("Skipping stack dump of cpu %d in hypervisor\n", 661 + cpu); 662 + break; 663 + case HV_ENOSYS: 664 + pr_warn("Hypervisor too old to allow remote stack dumps.\n"); 665 + goto skip_for_each; 666 + default: /* should not happen */ 667 + pr_warn("Skipping stack dump of cpu %d [%d,%#llx]\n", 668 + cpu, info[cpu].result, info[cpu].pc); 669 + break; 670 + } 671 + } 672 + skip_for_each: 673 + atomic_sub(cpumask_weight(&mask), &backtrace_cpus); 674 + } 675 + } 676 + #endif /* __tilegx_ */
+12
arch/tile/kernel/traps.c
··· 395 395 exception_exit(prev_state); 396 396 } 397 397 398 + void do_nmi(struct pt_regs *regs, int fault_num, unsigned long reason) 399 + { 400 + switch (reason) { 401 + case TILE_NMI_DUMP_STACK: 402 + do_nmi_dump_stack(regs); 403 + break; 404 + default: 405 + panic("Unexpected do_nmi type %ld", reason); 406 + return; 407 + } 408 + } 409 + 398 410 void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) 399 411 { 400 412 _dump_stack(dummy, pc, lr, sp, r52);