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

nmi_backtrace: generate one-line reports for idle cpus

When doing an nmi backtrace of many cores, most of which are idle, the
output is a little overwhelming and very uninformative. Suppress
messages for cpus that are idling when they are interrupted and just
emit one line, "NMI backtrace for N skipped: idling at pc 0xNNN".

We do this by grouping all the cpuidle code together into a new
.cpuidle.text section, and then checking the address of the interrupted
PC to see if it lies within that section.

This commit suitably tags x86 and tile idle routines, and only adds in
the minimal framework for other architectures.

Link: http://lkml.kernel.org/r/1472487169-14923-5-git-send-email-cmetcalf@mellanox.com
Signed-off-by: Chris Metcalf <cmetcalf@mellanox.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Daniel Thompson <daniel.thompson@linaro.org> [arm]
Tested-by: Petr Mladek <pmladek@suse.com>
Cc: Aaron Tomlin <atomlin@redhat.com>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Chris Metcalf and committed by
Linus Torvalds
6727ad9e 511f8389

+93 -22
+1
arch/alpha/kernel/vmlinux.lds.S
··· 22 22 HEAD_TEXT 23 23 TEXT_TEXT 24 24 SCHED_TEXT 25 + CPUIDLE_TEXT 25 26 LOCK_TEXT 26 27 *(.fixup) 27 28 *(.gnu.warning)
+1
arch/arc/kernel/vmlinux.lds.S
··· 89 89 _text = .; 90 90 TEXT_TEXT 91 91 SCHED_TEXT 92 + CPUIDLE_TEXT 92 93 LOCK_TEXT 93 94 KPROBES_TEXT 94 95 *(.fixup)
+1
arch/arm/kernel/vmlinux-xip.lds.S
··· 98 98 IRQENTRY_TEXT 99 99 TEXT_TEXT 100 100 SCHED_TEXT 101 + CPUIDLE_TEXT 101 102 LOCK_TEXT 102 103 KPROBES_TEXT 103 104 *(.gnu.warning)
+1
arch/arm/kernel/vmlinux.lds.S
··· 111 111 SOFTIRQENTRY_TEXT 112 112 TEXT_TEXT 113 113 SCHED_TEXT 114 + CPUIDLE_TEXT 114 115 LOCK_TEXT 115 116 HYPERVISOR_TEXT 116 117 KPROBES_TEXT
+1
arch/arm64/kernel/vmlinux.lds.S
··· 122 122 ENTRY_TEXT 123 123 TEXT_TEXT 124 124 SCHED_TEXT 125 + CPUIDLE_TEXT 125 126 LOCK_TEXT 126 127 KPROBES_TEXT 127 128 HYPERVISOR_TEXT
+1
arch/avr32/kernel/vmlinux.lds.S
··· 52 52 KPROBES_TEXT 53 53 TEXT_TEXT 54 54 SCHED_TEXT 55 + CPUIDLE_TEXT 55 56 LOCK_TEXT 56 57 *(.fixup) 57 58 *(.gnu.warning)
+1
arch/blackfin/kernel/vmlinux.lds.S
··· 33 33 #ifndef CONFIG_SCHEDULE_L1 34 34 SCHED_TEXT 35 35 #endif 36 + CPUIDLE_TEXT 36 37 LOCK_TEXT 37 38 IRQENTRY_TEXT 38 39 SOFTIRQENTRY_TEXT
+1
arch/c6x/kernel/vmlinux.lds.S
··· 70 70 _stext = .; 71 71 TEXT_TEXT 72 72 SCHED_TEXT 73 + CPUIDLE_TEXT 73 74 LOCK_TEXT 74 75 IRQENTRY_TEXT 75 76 SOFTIRQENTRY_TEXT
+1
arch/cris/kernel/vmlinux.lds.S
··· 43 43 HEAD_TEXT 44 44 TEXT_TEXT 45 45 SCHED_TEXT 46 + CPUIDLE_TEXT 46 47 LOCK_TEXT 47 48 *(.fixup) 48 49 *(.text.__*)
+1
arch/frv/kernel/vmlinux.lds.S
··· 63 63 *(.text..tlbmiss) 64 64 TEXT_TEXT 65 65 SCHED_TEXT 66 + CPUIDLE_TEXT 66 67 LOCK_TEXT 67 68 #ifdef CONFIG_DEBUG_INFO 68 69 INIT_TEXT
+1
arch/h8300/kernel/vmlinux.lds.S
··· 29 29 _stext = . ; 30 30 TEXT_TEXT 31 31 SCHED_TEXT 32 + CPUIDLE_TEXT 32 33 LOCK_TEXT 33 34 #if defined(CONFIG_ROMKERNEL) 34 35 *(.int_redirect)
+1
arch/hexagon/kernel/vmlinux.lds.S
··· 50 50 _text = .; 51 51 TEXT_TEXT 52 52 SCHED_TEXT 53 + CPUIDLE_TEXT 53 54 LOCK_TEXT 54 55 KPROBES_TEXT 55 56 *(.fixup)
+1
arch/ia64/kernel/vmlinux.lds.S
··· 46 46 __end_ivt_text = .; 47 47 TEXT_TEXT 48 48 SCHED_TEXT 49 + CPUIDLE_TEXT 49 50 LOCK_TEXT 50 51 KPROBES_TEXT 51 52 *(.gnu.linkonce.t*)
+1
arch/m32r/kernel/vmlinux.lds.S
··· 31 31 HEAD_TEXT 32 32 TEXT_TEXT 33 33 SCHED_TEXT 34 + CPUIDLE_TEXT 34 35 LOCK_TEXT 35 36 *(.fixup) 36 37 *(.gnu.warning)
+1
arch/m68k/kernel/vmlinux-nommu.lds
··· 45 45 HEAD_TEXT 46 46 TEXT_TEXT 47 47 SCHED_TEXT 48 + CPUIDLE_TEXT 48 49 LOCK_TEXT 49 50 *(.fixup) 50 51 . = ALIGN(16);
+1
arch/m68k/kernel/vmlinux-std.lds
··· 16 16 HEAD_TEXT 17 17 TEXT_TEXT 18 18 SCHED_TEXT 19 + CPUIDLE_TEXT 19 20 LOCK_TEXT 20 21 *(.fixup) 21 22 *(.gnu.warning)
+1
arch/m68k/kernel/vmlinux-sun3.lds
··· 16 16 HEAD_TEXT 17 17 TEXT_TEXT 18 18 SCHED_TEXT 19 + CPUIDLE_TEXT 19 20 LOCK_TEXT 20 21 *(.fixup) 21 22 *(.gnu.warning)
+1
arch/metag/kernel/vmlinux.lds.S
··· 21 21 .text : { 22 22 TEXT_TEXT 23 23 SCHED_TEXT 24 + CPUIDLE_TEXT 24 25 LOCK_TEXT 25 26 KPROBES_TEXT 26 27 IRQENTRY_TEXT
+1
arch/microblaze/kernel/vmlinux.lds.S
··· 33 33 EXIT_TEXT 34 34 EXIT_CALL 35 35 SCHED_TEXT 36 + CPUIDLE_TEXT 36 37 LOCK_TEXT 37 38 KPROBES_TEXT 38 39 IRQENTRY_TEXT
+1
arch/mips/kernel/vmlinux.lds.S
··· 55 55 .text : { 56 56 TEXT_TEXT 57 57 SCHED_TEXT 58 + CPUIDLE_TEXT 58 59 LOCK_TEXT 59 60 KPROBES_TEXT 60 61 IRQENTRY_TEXT
+1
arch/mn10300/kernel/vmlinux.lds.S
··· 30 30 HEAD_TEXT 31 31 TEXT_TEXT 32 32 SCHED_TEXT 33 + CPUIDLE_TEXT 33 34 LOCK_TEXT 34 35 KPROBES_TEXT 35 36 *(.fixup)
+1
arch/nios2/kernel/vmlinux.lds.S
··· 37 37 .text : { 38 38 TEXT_TEXT 39 39 SCHED_TEXT 40 + CPUIDLE_TEXT 40 41 LOCK_TEXT 41 42 IRQENTRY_TEXT 42 43 SOFTIRQENTRY_TEXT
+1
arch/openrisc/kernel/vmlinux.lds.S
··· 47 47 _stext = .; 48 48 TEXT_TEXT 49 49 SCHED_TEXT 50 + CPUIDLE_TEXT 50 51 LOCK_TEXT 51 52 KPROBES_TEXT 52 53 IRQENTRY_TEXT
+1
arch/parisc/kernel/vmlinux.lds.S
··· 69 69 .text ALIGN(PAGE_SIZE) : { 70 70 TEXT_TEXT 71 71 SCHED_TEXT 72 + CPUIDLE_TEXT 72 73 LOCK_TEXT 73 74 KPROBES_TEXT 74 75 IRQENTRY_TEXT
+1
arch/powerpc/kernel/vmlinux.lds.S
··· 52 52 /* careful! __ftr_alt_* sections need to be close to .text */ 53 53 *(.text .fixup __ftr_alt_* .ref.text) 54 54 SCHED_TEXT 55 + CPUIDLE_TEXT 55 56 LOCK_TEXT 56 57 KPROBES_TEXT 57 58 IRQENTRY_TEXT
+1
arch/s390/kernel/vmlinux.lds.S
··· 35 35 HEAD_TEXT 36 36 TEXT_TEXT 37 37 SCHED_TEXT 38 + CPUIDLE_TEXT 38 39 LOCK_TEXT 39 40 KPROBES_TEXT 40 41 IRQENTRY_TEXT
+1
arch/score/kernel/vmlinux.lds.S
··· 40 40 _text = .; /* Text and read-only data */ 41 41 TEXT_TEXT 42 42 SCHED_TEXT 43 + CPUIDLE_TEXT 43 44 LOCK_TEXT 44 45 KPROBES_TEXT 45 46 *(.text.*)
+1
arch/sh/kernel/vmlinux.lds.S
··· 36 36 TEXT_TEXT 37 37 EXTRA_TEXT 38 38 SCHED_TEXT 39 + CPUIDLE_TEXT 39 40 LOCK_TEXT 40 41 KPROBES_TEXT 41 42 IRQENTRY_TEXT
+1
arch/sparc/kernel/vmlinux.lds.S
··· 49 49 HEAD_TEXT 50 50 TEXT_TEXT 51 51 SCHED_TEXT 52 + CPUIDLE_TEXT 52 53 LOCK_TEXT 53 54 KPROBES_TEXT 54 55 IRQENTRY_TEXT
+1 -1
arch/tile/kernel/entry.S
··· 50 50 * When interrupted at _cpu_idle_nap, we bump the PC forward 8, and 51 51 * as a result return to the function that called _cpu_idle(). 52 52 */ 53 - STD_ENTRY(_cpu_idle) 53 + STD_ENTRY_SECTION(_cpu_idle, .cpuidle.text) 54 54 movei r1, 1 55 55 IRQ_ENABLE_LOAD(r2, r3) 56 56 mtspr INTERRUPT_CRITICAL_SECTION, r1
+1
arch/tile/kernel/vmlinux.lds.S
··· 42 42 .text : AT (ADDR(.text) - LOAD_OFFSET) { 43 43 HEAD_TEXT 44 44 SCHED_TEXT 45 + CPUIDLE_TEXT 45 46 LOCK_TEXT 46 47 KPROBES_TEXT 47 48 IRQENTRY_TEXT
+1
arch/um/kernel/dyn.lds.S
··· 68 68 _stext = .; 69 69 TEXT_TEXT 70 70 SCHED_TEXT 71 + CPUIDLE_TEXT 71 72 LOCK_TEXT 72 73 *(.fixup) 73 74 *(.stub .text.* .gnu.linkonce.t.*)
+1
arch/um/kernel/uml.lds.S
··· 28 28 _stext = .; 29 29 TEXT_TEXT 30 30 SCHED_TEXT 31 + CPUIDLE_TEXT 31 32 LOCK_TEXT 32 33 *(.fixup) 33 34 /* .gnu.warning sections are handled specially by elf32.em. */
+1
arch/unicore32/kernel/vmlinux.lds.S
··· 37 37 .text : { /* Real text segment */ 38 38 TEXT_TEXT 39 39 SCHED_TEXT 40 + CPUIDLE_TEXT 40 41 LOCK_TEXT 41 42 42 43 *(.fixup)
+8 -4
arch/x86/include/asm/irqflags.h
··· 4 4 #include <asm/processor-flags.h> 5 5 6 6 #ifndef __ASSEMBLY__ 7 + 8 + /* Provide __cpuidle; we can't safely include <linux/cpu.h> */ 9 + #define __cpuidle __attribute__((__section__(".cpuidle.text"))) 10 + 7 11 /* 8 12 * Interrupt control: 9 13 */ ··· 48 44 asm volatile("sti": : :"memory"); 49 45 } 50 46 51 - static inline void native_safe_halt(void) 47 + static inline __cpuidle void native_safe_halt(void) 52 48 { 53 49 asm volatile("sti; hlt": : :"memory"); 54 50 } 55 51 56 - static inline void native_halt(void) 52 + static inline __cpuidle void native_halt(void) 57 53 { 58 54 asm volatile("hlt": : :"memory"); 59 55 } ··· 90 86 * Used in the idle loop; sti takes one instruction cycle 91 87 * to complete: 92 88 */ 93 - static inline void arch_safe_halt(void) 89 + static inline __cpuidle void arch_safe_halt(void) 94 90 { 95 91 native_safe_halt(); 96 92 } ··· 99 95 * Used when interrupts are already enabled or to 100 96 * shutdown the processor: 101 97 */ 102 - static inline void halt(void) 98 + static inline __cpuidle void halt(void) 103 99 { 104 100 native_halt(); 105 101 }
+1 -1
arch/x86/kernel/acpi/cstate.c
··· 152 152 } 153 153 EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); 154 154 155 - void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) 155 + void __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) 156 156 { 157 157 unsigned int cpu = smp_processor_id(); 158 158 struct cstate_entry *percpu_entry;
+2 -2
arch/x86/kernel/process.c
··· 302 302 /* 303 303 * We use this if we don't have any better idle routine.. 304 304 */ 305 - void default_idle(void) 305 + void __cpuidle default_idle(void) 306 306 { 307 307 trace_cpu_idle_rcuidle(1, smp_processor_id()); 308 308 safe_halt(); ··· 417 417 * with interrupts enabled and no flags, which is backwards compatible with the 418 418 * original MWAIT implementation. 419 419 */ 420 - static void mwait_idle(void) 420 + static __cpuidle void mwait_idle(void) 421 421 { 422 422 if (!current_set_polling_and_test()) { 423 423 trace_cpu_idle_rcuidle(1, smp_processor_id());
+1
arch/x86/kernel/vmlinux.lds.S
··· 97 97 _stext = .; 98 98 TEXT_TEXT 99 99 SCHED_TEXT 100 + CPUIDLE_TEXT 100 101 LOCK_TEXT 101 102 KPROBES_TEXT 102 103 ENTRY_TEXT
+3
arch/xtensa/kernel/vmlinux.lds.S
··· 89 89 VMLINUX_SYMBOL(__sched_text_start) = .; 90 90 *(.sched.literal .sched.text) 91 91 VMLINUX_SYMBOL(__sched_text_end) = .; 92 + VMLINUX_SYMBOL(__cpuidle_text_start) = .; 93 + *(.cpuidle.literal .cpuidle.text) 94 + VMLINUX_SYMBOL(__cpuidle_text_end) = .; 92 95 VMLINUX_SYMBOL(__lock_text_start) = .; 93 96 *(.spinlock.literal .spinlock.text) 94 97 VMLINUX_SYMBOL(__lock_text_end) = .;
+3 -2
drivers/acpi/processor_idle.c
··· 31 31 #include <linux/sched.h> /* need_resched() */ 32 32 #include <linux/tick.h> 33 33 #include <linux/cpuidle.h> 34 + #include <linux/cpu.h> 34 35 #include <acpi/processor.h> 35 36 36 37 /* ··· 116 115 * Callers should disable interrupts before the call and enable 117 116 * interrupts after return. 118 117 */ 119 - static void acpi_safe_halt(void) 118 + static void __cpuidle acpi_safe_halt(void) 120 119 { 121 120 if (!tif_need_resched()) { 122 121 safe_halt(); ··· 646 645 * 647 646 * Caller disables interrupt before call and enables interrupt after return. 648 647 */ 649 - static void acpi_idle_do_entry(struct acpi_processor_cx *cx) 648 + static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx) 650 649 { 651 650 if (cx->entry_method == ACPI_CSTATE_FFH) { 652 651 /* Call into architectural FFH based C-state */
+3 -2
drivers/cpuidle/driver.c
··· 14 14 #include <linux/cpuidle.h> 15 15 #include <linux/cpumask.h> 16 16 #include <linux/tick.h> 17 + #include <linux/cpu.h> 17 18 18 19 #include "cpuidle.h" 19 20 ··· 179 178 } 180 179 181 180 #ifdef CONFIG_ARCH_HAS_CPU_RELAX 182 - static int poll_idle(struct cpuidle_device *dev, 183 - struct cpuidle_driver *drv, int index) 181 + static int __cpuidle poll_idle(struct cpuidle_device *dev, 182 + struct cpuidle_driver *drv, int index) 184 183 { 185 184 local_irq_enable(); 186 185 if (!current_set_polling_and_test()) {
+2 -2
drivers/idle/intel_idle.c
··· 863 863 * 864 864 * Must be called under local_irq_disable(). 865 865 */ 866 - static int intel_idle(struct cpuidle_device *dev, 867 - struct cpuidle_driver *drv, int index) 866 + static __cpuidle int intel_idle(struct cpuidle_device *dev, 867 + struct cpuidle_driver *drv, int index) 868 868 { 869 869 unsigned long ecx = 1; /* break on interrupt flag */ 870 870 struct cpuidle_state *state = &drv->states[index];
+6
include/asm-generic/vmlinux.lds.h
··· 454 454 *(.spinlock.text) \ 455 455 VMLINUX_SYMBOL(__lock_text_end) = .; 456 456 457 + #define CPUIDLE_TEXT \ 458 + ALIGN_FUNCTION(); \ 459 + VMLINUX_SYMBOL(__cpuidle_text_start) = .; \ 460 + *(.cpuidle.text) \ 461 + VMLINUX_SYMBOL(__cpuidle_text_end) = .; 462 + 457 463 #define KPROBES_TEXT \ 458 464 ALIGN_FUNCTION(); \ 459 465 VMLINUX_SYMBOL(__kprobes_text_start) = .; \
+5
include/linux/cpu.h
··· 231 231 232 232 void cpu_idle_poll_ctrl(bool enable); 233 233 234 + /* Attach to any functions which should be considered cpuidle. */ 235 + #define __cpuidle __attribute__((__section__(".cpuidle.text"))) 236 + 237 + bool cpu_in_idle(unsigned long pc); 238 + 234 239 void arch_cpu_idle(void); 235 240 void arch_cpu_idle_prepare(void); 236 241 void arch_cpu_idle_enter(void);
+11 -2
kernel/sched/idle.c
··· 16 16 17 17 #include "sched.h" 18 18 19 + /* Linker adds these: start and end of __cpuidle functions */ 20 + extern char __cpuidle_text_start[], __cpuidle_text_end[]; 21 + 19 22 /** 20 23 * sched_idle_set_state - Record idle state for the current CPU. 21 24 * @idle_state: State to record. ··· 56 53 __setup("hlt", cpu_idle_nopoll_setup); 57 54 #endif 58 55 59 - static inline int cpu_idle_poll(void) 56 + static noinline int __cpuidle cpu_idle_poll(void) 60 57 { 61 58 rcu_idle_enter(); 62 59 trace_cpu_idle_rcuidle(0, smp_processor_id()); ··· 87 84 * 88 85 * To use when the cpuidle framework cannot be used. 89 86 */ 90 - void default_idle_call(void) 87 + void __cpuidle default_idle_call(void) 91 88 { 92 89 if (current_clr_polling_and_test()) { 93 90 local_irq_enable(); ··· 272 269 sched_ttwu_pending(); 273 270 schedule_preempt_disabled(); 274 271 } 272 + } 273 + 274 + bool cpu_in_idle(unsigned long pc) 275 + { 276 + return pc >= (unsigned long)__cpuidle_text_start && 277 + pc < (unsigned long)__cpuidle_text_end; 275 278 } 276 279 277 280 void cpu_startup_entry(enum cpuhp_state state)
+11 -5
lib/nmi_backtrace.c
··· 16 16 #include <linux/delay.h> 17 17 #include <linux/kprobes.h> 18 18 #include <linux/nmi.h> 19 + #include <linux/cpu.h> 19 20 20 21 #ifdef arch_trigger_cpumask_backtrace 21 22 /* For reliability, we're prepared to waste bits here. */ ··· 88 87 int cpu = smp_processor_id(); 89 88 90 89 if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { 91 - pr_warn("NMI backtrace for cpu %d\n", cpu); 92 - if (regs) 93 - show_regs(regs); 94 - else 95 - dump_stack(); 90 + if (regs && cpu_in_idle(instruction_pointer(regs))) { 91 + pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n", 92 + cpu, instruction_pointer(regs)); 93 + } else { 94 + pr_warn("NMI backtrace for cpu %d\n", cpu); 95 + if (regs) 96 + show_regs(regs); 97 + else 98 + dump_stack(); 99 + } 96 100 cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); 97 101 return true; 98 102 }
+1 -1
scripts/mod/modpost.c
··· 888 888 889 889 #define DATA_SECTIONS ".data", ".data.rel" 890 890 #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \ 891 - ".kprobes.text" 891 + ".kprobes.text", ".cpuidle.text" 892 892 #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ 893 893 ".fixup", ".entry.text", ".exception.text", ".text.*", \ 894 894 ".coldtext"
+1
scripts/recordmcount.c
··· 365 365 strcmp(".irqentry.text", txtname) == 0 || 366 366 strcmp(".softirqentry.text", txtname) == 0 || 367 367 strcmp(".kprobes.text", txtname) == 0 || 368 + strcmp(".cpuidle.text", txtname) == 0 || 368 369 strcmp(".text.unlikely", txtname) == 0; 369 370 } 370 371
+1
scripts/recordmcount.pl
··· 136 136 ".irqentry.text" => 1, 137 137 ".softirqentry.text" => 1, 138 138 ".kprobes.text" => 1, 139 + ".cpuidle.text" => 1, 139 140 ".text.unlikely" => 1, 140 141 ); 141 142