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>
                HEAD_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                *(.fixup)
                *(.gnu.warning)
 
                _text = .;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                *(.fixup)
 
                        IRQENTRY_TEXT
                        TEXT_TEXT
                        SCHED_TEXT
+                       CPUIDLE_TEXT
                        LOCK_TEXT
                        KPROBES_TEXT
                        *(.gnu.warning)
 
                        SOFTIRQENTRY_TEXT
                        TEXT_TEXT
                        SCHED_TEXT
+                       CPUIDLE_TEXT
                        LOCK_TEXT
                        HYPERVISOR_TEXT
                        KPROBES_TEXT
 
                        ENTRY_TEXT
                        TEXT_TEXT
                        SCHED_TEXT
+                       CPUIDLE_TEXT
                        LOCK_TEXT
                        KPROBES_TEXT
                        HYPERVISOR_TEXT
 
                KPROBES_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                *(.fixup)
                *(.gnu.warning)
 
 #ifndef CONFIG_SCHEDULE_L1
                SCHED_TEXT
 #endif
+               CPUIDLE_TEXT
                LOCK_TEXT
                IRQENTRY_TEXT
                SOFTIRQENTRY_TEXT
 
                _stext = .;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                IRQENTRY_TEXT
                SOFTIRQENTRY_TEXT
 
                HEAD_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                *(.fixup)
                *(.text.__*)
 
        *(.text..tlbmiss)
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
 #ifdef CONFIG_DEBUG_INFO
        INIT_TEXT
 
        _stext = . ;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
 #if defined(CONFIG_ROMKERNEL)
                *(.int_redirect)
 
                _text = .;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                *(.fixup)
 
                __end_ivt_text = .;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                *(.gnu.linkonce.t*)
 
        HEAD_TEXT
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
        *(.fixup)
        *(.gnu.warning)
 
                HEAD_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                *(.fixup)
                . = ALIGN(16);
 
        HEAD_TEXT
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
        *(.fixup)
        *(.gnu.warning)
 
        HEAD_TEXT
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
        *(.fixup)
        *(.gnu.warning)
 
   .text : {
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
        KPROBES_TEXT
        IRQENTRY_TEXT
 
                EXIT_TEXT
                EXIT_CALL
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
        .text : {
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
        HEAD_TEXT
        TEXT_TEXT
        SCHED_TEXT
+       CPUIDLE_TEXT
        LOCK_TEXT
        KPROBES_TEXT
        *(.fixup)
 
        .text : {
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                IRQENTRY_TEXT
                SOFTIRQENTRY_TEXT
 
           _stext = .;
          TEXT_TEXT
          SCHED_TEXT
+         CPUIDLE_TEXT
          LOCK_TEXT
          KPROBES_TEXT
          IRQENTRY_TEXT
 
        .text ALIGN(PAGE_SIZE) : {
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
                /* careful! __ftr_alt_* sections need to be close to .text */
                *(.text .fixup __ftr_alt_* .ref.text)
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
                HEAD_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
                _text = .;      /* Text and read-only data */
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                *(.text.*)
 
                TEXT_TEXT
                EXTRA_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
                HEAD_TEXT
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                IRQENTRY_TEXT
 
  * When interrupted at _cpu_idle_nap, we bump the PC forward 8, and
  * as a result return to the function that called _cpu_idle().
  */
-STD_ENTRY(_cpu_idle)
+STD_ENTRY_SECTION(_cpu_idle, .cpuidle.text)
        movei r1, 1
        IRQ_ENABLE_LOAD(r2, r3)
        mtspr INTERRUPT_CRITICAL_SECTION, r1
 
   .text : AT (ADDR(.text) - LOAD_OFFSET) {
     HEAD_TEXT
     SCHED_TEXT
+    CPUIDLE_TEXT
     LOCK_TEXT
     KPROBES_TEXT
     IRQENTRY_TEXT
 
     _stext = .;
     TEXT_TEXT
     SCHED_TEXT
+    CPUIDLE_TEXT
     LOCK_TEXT
     *(.fixup)
     *(.stub .text.* .gnu.linkonce.t.*)
 
     _stext = .;
     TEXT_TEXT
     SCHED_TEXT
+    CPUIDLE_TEXT
     LOCK_TEXT
     *(.fixup)
     /* .gnu.warning sections are handled specially by elf32.em.  */
 
        .text : {               /* Real text segment */
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
 
                *(.fixup)
 
 #include <asm/processor-flags.h>
 
 #ifndef __ASSEMBLY__
+
+/* Provide __cpuidle; we can't safely include <linux/cpu.h> */
+#define __cpuidle __attribute__((__section__(".cpuidle.text")))
+
 /*
  * Interrupt control:
  */
        asm volatile("sti": : :"memory");
 }
 
-static inline void native_safe_halt(void)
+static inline __cpuidle void native_safe_halt(void)
 {
        asm volatile("sti; hlt": : :"memory");
 }
 
-static inline void native_halt(void)
+static inline __cpuidle void native_halt(void)
 {
        asm volatile("hlt": : :"memory");
 }
  * Used in the idle loop; sti takes one instruction cycle
  * to complete:
  */
-static inline void arch_safe_halt(void)
+static inline __cpuidle void arch_safe_halt(void)
 {
        native_safe_halt();
 }
  * Used when interrupts are already enabled or to
  * shutdown the processor:
  */
-static inline void halt(void)
+static inline __cpuidle void halt(void)
 {
        native_halt();
 }
 
 }
 EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe);
 
-void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
+void __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
 {
        unsigned int cpu = smp_processor_id();
        struct cstate_entry *percpu_entry;
 
 /*
  * We use this if we don't have any better idle routine..
  */
-void default_idle(void)
+void __cpuidle default_idle(void)
 {
        trace_cpu_idle_rcuidle(1, smp_processor_id());
        safe_halt();
  * with interrupts enabled and no flags, which is backwards compatible with the
  * original MWAIT implementation.
  */
-static void mwait_idle(void)
+static __cpuidle void mwait_idle(void)
 {
        if (!current_set_polling_and_test()) {
                trace_cpu_idle_rcuidle(1, smp_processor_id());
 
                _stext = .;
                TEXT_TEXT
                SCHED_TEXT
+               CPUIDLE_TEXT
                LOCK_TEXT
                KPROBES_TEXT
                ENTRY_TEXT
 
     VMLINUX_SYMBOL(__sched_text_start) = .;
     *(.sched.literal .sched.text)
     VMLINUX_SYMBOL(__sched_text_end) = .;
+    VMLINUX_SYMBOL(__cpuidle_text_start) = .;
+    *(.cpuidle.literal .cpuidle.text)
+    VMLINUX_SYMBOL(__cpuidle_text_end) = .;
     VMLINUX_SYMBOL(__lock_text_start) = .;
     *(.spinlock.literal .spinlock.text)
     VMLINUX_SYMBOL(__lock_text_end) = .;
 
 #include <linux/sched.h>       /* need_resched() */
 #include <linux/tick.h>
 #include <linux/cpuidle.h>
+#include <linux/cpu.h>
 #include <acpi/processor.h>
 
 /*
  * Callers should disable interrupts before the call and enable
  * interrupts after return.
  */
-static void acpi_safe_halt(void)
+static void __cpuidle acpi_safe_halt(void)
 {
        if (!tif_need_resched()) {
                safe_halt();
  *
  * Caller disables interrupt before call and enables interrupt after return.
  */
-static void acpi_idle_do_entry(struct acpi_processor_cx *cx)
+static void __cpuidle acpi_idle_do_entry(struct acpi_processor_cx *cx)
 {
        if (cx->entry_method == ACPI_CSTATE_FFH) {
                /* Call into architectural FFH based C-state */
 
 #include <linux/cpuidle.h>
 #include <linux/cpumask.h>
 #include <linux/tick.h>
+#include <linux/cpu.h>
 
 #include "cpuidle.h"
 
 }
 
 #ifdef CONFIG_ARCH_HAS_CPU_RELAX
-static int poll_idle(struct cpuidle_device *dev,
-               struct cpuidle_driver *drv, int index)
+static int __cpuidle poll_idle(struct cpuidle_device *dev,
+                              struct cpuidle_driver *drv, int index)
 {
        local_irq_enable();
        if (!current_set_polling_and_test()) {
 
  *
  * Must be called under local_irq_disable().
  */
-static int intel_idle(struct cpuidle_device *dev,
-               struct cpuidle_driver *drv, int index)
+static __cpuidle int intel_idle(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index)
 {
        unsigned long ecx = 1; /* break on interrupt flag */
        struct cpuidle_state *state = &drv->states[index];
 
                *(.spinlock.text)                                       \
                VMLINUX_SYMBOL(__lock_text_end) = .;
 
+#define CPUIDLE_TEXT                                                   \
+               ALIGN_FUNCTION();                                       \
+               VMLINUX_SYMBOL(__cpuidle_text_start) = .;               \
+               *(.cpuidle.text)                                        \
+               VMLINUX_SYMBOL(__cpuidle_text_end) = .;
+
 #define KPROBES_TEXT                                                   \
                ALIGN_FUNCTION();                                       \
                VMLINUX_SYMBOL(__kprobes_text_start) = .;               \
 
 
 void cpu_idle_poll_ctrl(bool enable);
 
+/* Attach to any functions which should be considered cpuidle. */
+#define __cpuidle      __attribute__((__section__(".cpuidle.text")))
+
+bool cpu_in_idle(unsigned long pc);
+
 void arch_cpu_idle(void);
 void arch_cpu_idle_prepare(void);
 void arch_cpu_idle_enter(void);
 
 
 #include "sched.h"
 
+/* Linker adds these: start and end of __cpuidle functions */
+extern char __cpuidle_text_start[], __cpuidle_text_end[];
+
 /**
  * sched_idle_set_state - Record idle state for the current CPU.
  * @idle_state: State to record.
 __setup("hlt", cpu_idle_nopoll_setup);
 #endif
 
-static inline int cpu_idle_poll(void)
+static noinline int __cpuidle cpu_idle_poll(void)
 {
        rcu_idle_enter();
        trace_cpu_idle_rcuidle(0, smp_processor_id());
  *
  * To use when the cpuidle framework cannot be used.
  */
-void default_idle_call(void)
+void __cpuidle default_idle_call(void)
 {
        if (current_clr_polling_and_test()) {
                local_irq_enable();
        }
 }
 
+bool cpu_in_idle(unsigned long pc)
+{
+       return pc >= (unsigned long)__cpuidle_text_start &&
+               pc < (unsigned long)__cpuidle_text_end;
+}
+
 void cpu_startup_entry(enum cpuhp_state state)
 {
        /*
 
 #include <linux/delay.h>
 #include <linux/kprobes.h>
 #include <linux/nmi.h>
+#include <linux/cpu.h>
 
 #ifdef arch_trigger_cpumask_backtrace
 /* For reliability, we're prepared to waste bits here. */
        int cpu = smp_processor_id();
 
        if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
-               pr_warn("NMI backtrace for cpu %d\n", cpu);
-               if (regs)
-                       show_regs(regs);
-               else
-                       dump_stack();
+               if (regs && cpu_in_idle(instruction_pointer(regs))) {
+                       pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n",
+                               cpu, instruction_pointer(regs));
+               } else {
+                       pr_warn("NMI backtrace for cpu %d\n", cpu);
+                       if (regs)
+                               show_regs(regs);
+                       else
+                               dump_stack();
+               }
                cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
                return true;
        }
 
 
 #define DATA_SECTIONS ".data", ".data.rel"
 #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \
-               ".kprobes.text"
+               ".kprobes.text", ".cpuidle.text"
 #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
                ".fixup", ".entry.text", ".exception.text", ".text.*", \
                ".coldtext"
 
                strcmp(".irqentry.text", txtname) == 0 ||
                strcmp(".softirqentry.text", txtname) == 0 ||
                strcmp(".kprobes.text", txtname) == 0 ||
+               strcmp(".cpuidle.text", txtname) == 0 ||
                strcmp(".text.unlikely", txtname) == 0;
 }
 
 
      ".irqentry.text" => 1,
      ".softirqentry.text" => 1,
      ".kprobes.text" => 1,
+     ".cpuidle.text" => 1,
      ".text.unlikely" => 1,
 );