aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@mellanox.com>2016-10-07 20:02:45 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-07 21:46:30 -0400
commit9a01c3ed5cdb35d9004eb92510ee6ea11b4a5f16 (patch)
tree0a1cf51c3965ed0c820d33d5d7ed636dd5a968a0
parent589a9785ee3a7cb85f1dedc3dad1c9754c691880 (diff)
nmi_backtrace: add more trigger_*_cpu_backtrace() methods
Patch series "improvements to the nmi_backtrace code" v9. This patch series modifies the trigger_xxx_backtrace() NMI-based remote backtracing code to make it more flexible, and makes a few small improvements along the way. The motivation comes from the task isolation code, where there are scenarios where we want to be able to diagnose a case where some cpu is about to interrupt a task-isolated cpu. It can be helpful to see both where the interrupting cpu is, and also an approximation of where the cpu that is being interrupted is. The nmi_backtrace framework allows us to discover the stack of the interrupted cpu. I've tested that the change works as desired on tile, and build-tested x86, arm, mips, and sparc64. For x86 I confirmed that the generic cpuidle stuff as well as the architecture-specific routines are in the new cpuidle section. For arm, mips, and sparc I just build-tested it and made sure the generic cpuidle routines were in the new cpuidle section, but I didn't attempt to figure out which the platform-specific idle routines might be. That might be more usefully done by someone with platform experience in follow-up patches. This patch (of 4): Currently you can only request a backtrace of either all cpus, or all cpus but yourself. It can also be helpful to request a remote backtrace of a single cpu, and since we want that, the logical extension is to support a cpumask as the underlying primitive. This change modifies the existing lib/nmi_backtrace.c code to take a cpumask as its basic primitive, and modifies the linux/nmi.h code to use the new "cpumask" method instead. The existing clients of nmi_backtrace (arm and x86) are converted to using the new cpumask approach in this change. The other users of the backtracing API (sparc64 and mips) are converted to use the cpumask approach rather than the all/allbutself approach. The mips code ignored the "include_self" boolean but with this change it will now also dump a local backtrace if requested. Link: http://lkml.kernel.org/r/1472487169-14923-2-git-send-email-cmetcalf@mellanox.com Signed-off-by: Chris Metcalf <cmetcalf@mellanox.com> Tested-by: Daniel Thompson <daniel.thompson@linaro.org> [arm] Reviewed-by: Aaron Tomlin <atomlin@redhat.com> Reviewed-by: Petr Mladek <pmladek@suse.com> 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> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: David Miller <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/arm/include/asm/irq.h5
-rw-r--r--arch/arm/kernel/smp.c4
-rw-r--r--arch/mips/include/asm/irq.h5
-rw-r--r--arch/mips/kernel/process.c11
-rw-r--r--arch/sparc/include/asm/irq_64.h5
-rw-r--r--arch/sparc/kernel/process_64.c10
-rw-r--r--arch/x86/include/asm/irq.h5
-rw-r--r--arch/x86/kernel/apic/hw_nmi.c18
-rw-r--r--include/linux/nmi.h31
-rw-r--r--lib/nmi_backtrace.c17
10 files changed, 72 insertions, 39 deletions
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index 1bd9510de1b9..e53638c8ed8a 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -36,8 +36,9 @@ extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
36#endif 36#endif
37 37
38#ifdef CONFIG_SMP 38#ifdef CONFIG_SMP
39extern void arch_trigger_all_cpu_backtrace(bool); 39extern void arch_trigger_cpumask_backtrace(const cpumask_t *mask,
40#define arch_trigger_all_cpu_backtrace(x) arch_trigger_all_cpu_backtrace(x) 40 bool exclude_self);
41#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
41#endif 42#endif
42 43
43static inline int nr_legacy_irqs(void) 44static inline int nr_legacy_irqs(void)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 937c8920d741..5abc5697e4e5 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -760,7 +760,7 @@ static void raise_nmi(cpumask_t *mask)
760 smp_cross_call(mask, IPI_CPU_BACKTRACE); 760 smp_cross_call(mask, IPI_CPU_BACKTRACE);
761} 761}
762 762
763void arch_trigger_all_cpu_backtrace(bool include_self) 763void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
764{ 764{
765 nmi_trigger_all_cpu_backtrace(include_self, raise_nmi); 765 nmi_trigger_cpumask_backtrace(mask, exclude_self, raise_nmi);
766} 766}
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 15e0fecbc300..6bf10e796553 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -51,7 +51,8 @@ extern int cp0_fdc_irq;
51 51
52extern int get_c0_fdc_int(void); 52extern int get_c0_fdc_int(void);
53 53
54void arch_trigger_all_cpu_backtrace(bool); 54void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
55#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace 55 bool exclude_self);
56#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
56 57
57#endif /* _ASM_IRQ_H */ 58#endif /* _ASM_IRQ_H */
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index d2d061520a23..9514e5f2209f 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -569,9 +569,16 @@ static void arch_dump_stack(void *info)
569 dump_stack(); 569 dump_stack();
570} 570}
571 571
572void arch_trigger_all_cpu_backtrace(bool include_self) 572void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
573{ 573{
574 smp_call_function(arch_dump_stack, NULL, 1); 574 long this_cpu = get_cpu();
575
576 if (cpumask_test_cpu(this_cpu, mask) && !exclude_self)
577 dump_stack();
578
579 smp_call_function_many(mask, arch_dump_stack, NULL, 1);
580
581 put_cpu();
575} 582}
576 583
577int mips_get_process_fp_mode(struct task_struct *task) 584int mips_get_process_fp_mode(struct task_struct *task)
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h
index 3f70f900e834..1d51a11fb261 100644
--- a/arch/sparc/include/asm/irq_64.h
+++ b/arch/sparc/include/asm/irq_64.h
@@ -86,8 +86,9 @@ static inline unsigned long get_softint(void)
86 return retval; 86 return retval;
87} 87}
88 88
89void arch_trigger_all_cpu_backtrace(bool); 89void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
90#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace 90 bool exclude_self);
91#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
91 92
92extern void *hardirq_stack[NR_CPUS]; 93extern void *hardirq_stack[NR_CPUS];
93extern void *softirq_stack[NR_CPUS]; 94extern void *softirq_stack[NR_CPUS];
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c
index fa14402b33f9..47ff5588e521 100644
--- a/arch/sparc/kernel/process_64.c
+++ b/arch/sparc/kernel/process_64.c
@@ -239,7 +239,7 @@ static void __global_reg_poll(struct global_reg_snapshot *gp)
239 } 239 }
240} 240}
241 241
242void arch_trigger_all_cpu_backtrace(bool include_self) 242void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
243{ 243{
244 struct thread_info *tp = current_thread_info(); 244 struct thread_info *tp = current_thread_info();
245 struct pt_regs *regs = get_irq_regs(); 245 struct pt_regs *regs = get_irq_regs();
@@ -255,15 +255,15 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
255 255
256 memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); 256 memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
257 257
258 if (include_self) 258 if (cpumask_test_cpu(this_cpu, mask) && !exclude_self)
259 __global_reg_self(tp, regs, this_cpu); 259 __global_reg_self(tp, regs, this_cpu);
260 260
261 smp_fetch_global_regs(); 261 smp_fetch_global_regs();
262 262
263 for_each_online_cpu(cpu) { 263 for_each_cpu(cpu, mask) {
264 struct global_reg_snapshot *gp; 264 struct global_reg_snapshot *gp;
265 265
266 if (!include_self && cpu == this_cpu) 266 if (exclude_self && cpu == this_cpu)
267 continue; 267 continue;
268 268
269 gp = &global_cpu_snapshot[cpu].reg; 269 gp = &global_cpu_snapshot[cpu].reg;
@@ -300,7 +300,7 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
300 300
301static void sysrq_handle_globreg(int key) 301static void sysrq_handle_globreg(int key)
302{ 302{
303 arch_trigger_all_cpu_backtrace(true); 303 trigger_all_cpu_backtrace();
304} 304}
305 305
306static struct sysrq_key_op sparc_globalreg_op = { 306static struct sysrq_key_op sparc_globalreg_op = {
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
index e7de5c9a4fbd..16d3fa211962 100644
--- a/arch/x86/include/asm/irq.h
+++ b/arch/x86/include/asm/irq.h
@@ -50,8 +50,9 @@ extern int vector_used_by_percpu_irq(unsigned int vector);
50extern void init_ISA_irqs(void); 50extern void init_ISA_irqs(void);
51 51
52#ifdef CONFIG_X86_LOCAL_APIC 52#ifdef CONFIG_X86_LOCAL_APIC
53void arch_trigger_all_cpu_backtrace(bool); 53void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
54#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace 54 bool exclude_self);
55#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
55#endif 56#endif
56 57
57#endif /* _ASM_X86_IRQ_H */ 58#endif /* _ASM_X86_IRQ_H */
diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c
index f29501e1a5c1..c73c9fb281e1 100644
--- a/arch/x86/kernel/apic/hw_nmi.c
+++ b/arch/x86/kernel/apic/hw_nmi.c
@@ -26,32 +26,32 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)
26} 26}
27#endif 27#endif
28 28
29#ifdef arch_trigger_all_cpu_backtrace 29#ifdef arch_trigger_cpumask_backtrace
30static void nmi_raise_cpu_backtrace(cpumask_t *mask) 30static void nmi_raise_cpu_backtrace(cpumask_t *mask)
31{ 31{
32 apic->send_IPI_mask(mask, NMI_VECTOR); 32 apic->send_IPI_mask(mask, NMI_VECTOR);
33} 33}
34 34
35void arch_trigger_all_cpu_backtrace(bool include_self) 35void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
36{ 36{
37 nmi_trigger_all_cpu_backtrace(include_self, nmi_raise_cpu_backtrace); 37 nmi_trigger_cpumask_backtrace(mask, exclude_self,
38 nmi_raise_cpu_backtrace);
38} 39}
39 40
40static int 41static int nmi_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
41arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
42{ 42{
43 if (nmi_cpu_backtrace(regs)) 43 if (nmi_cpu_backtrace(regs))
44 return NMI_HANDLED; 44 return NMI_HANDLED;
45 45
46 return NMI_DONE; 46 return NMI_DONE;
47} 47}
48NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler); 48NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler);
49 49
50static int __init register_trigger_all_cpu_backtrace(void) 50static int __init register_nmi_cpu_backtrace_handler(void)
51{ 51{
52 register_nmi_handler(NMI_LOCAL, arch_trigger_all_cpu_backtrace_handler, 52 register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler,
53 0, "arch_bt"); 53 0, "arch_bt");
54 return 0; 54 return 0;
55} 55}
56early_initcall(register_trigger_all_cpu_backtrace); 56early_initcall(register_nmi_cpu_backtrace_handler);
57#endif 57#endif
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 4630eeae18e0..a78c35cff1ae 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -35,21 +35,34 @@ static inline void hardlockup_detector_disable(void) {}
35 * base function. Return whether such support was available, 35 * base function. Return whether such support was available,
36 * to allow calling code to fall back to some other mechanism: 36 * to allow calling code to fall back to some other mechanism:
37 */ 37 */
38#ifdef arch_trigger_all_cpu_backtrace 38#ifdef arch_trigger_cpumask_backtrace
39static inline bool trigger_all_cpu_backtrace(void) 39static inline bool trigger_all_cpu_backtrace(void)
40{ 40{
41 arch_trigger_all_cpu_backtrace(true); 41 arch_trigger_cpumask_backtrace(cpu_online_mask, false);
42
43 return true; 42 return true;
44} 43}
44
45static inline bool trigger_allbutself_cpu_backtrace(void) 45static inline bool trigger_allbutself_cpu_backtrace(void)
46{ 46{
47 arch_trigger_all_cpu_backtrace(false); 47 arch_trigger_cpumask_backtrace(cpu_online_mask, true);
48 return true;
49}
50
51static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
52{
53 arch_trigger_cpumask_backtrace(mask, false);
54 return true;
55}
56
57static inline bool trigger_single_cpu_backtrace(int cpu)
58{
59 arch_trigger_cpumask_backtrace(cpumask_of(cpu), false);
48 return true; 60 return true;
49} 61}
50 62
51/* generic implementation */ 63/* generic implementation */
52void nmi_trigger_all_cpu_backtrace(bool include_self, 64void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
65 bool exclude_self,
53 void (*raise)(cpumask_t *mask)); 66 void (*raise)(cpumask_t *mask));
54bool nmi_cpu_backtrace(struct pt_regs *regs); 67bool nmi_cpu_backtrace(struct pt_regs *regs);
55 68
@@ -62,6 +75,14 @@ static inline bool trigger_allbutself_cpu_backtrace(void)
62{ 75{
63 return false; 76 return false;
64} 77}
78static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
79{
80 return false;
81}
82static inline bool trigger_single_cpu_backtrace(int cpu)
83{
84 return false;
85}
65#endif 86#endif
66 87
67#ifdef CONFIG_LOCKUP_DETECTOR 88#ifdef CONFIG_LOCKUP_DETECTOR
diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c
index 26caf51cc238..df347e355267 100644
--- a/lib/nmi_backtrace.c
+++ b/lib/nmi_backtrace.c
@@ -17,20 +17,21 @@
17#include <linux/kprobes.h> 17#include <linux/kprobes.h>
18#include <linux/nmi.h> 18#include <linux/nmi.h>
19 19
20#ifdef arch_trigger_all_cpu_backtrace 20#ifdef arch_trigger_cpumask_backtrace
21/* For reliability, we're prepared to waste bits here. */ 21/* For reliability, we're prepared to waste bits here. */
22static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; 22static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly;
23 23
24/* "in progress" flag of arch_trigger_all_cpu_backtrace */ 24/* "in progress" flag of arch_trigger_cpumask_backtrace */
25static unsigned long backtrace_flag; 25static unsigned long backtrace_flag;
26 26
27/* 27/*
28 * When raise() is called it will be is passed a pointer to the 28 * When raise() is called it will be passed a pointer to the
29 * backtrace_mask. Architectures that call nmi_cpu_backtrace() 29 * backtrace_mask. Architectures that call nmi_cpu_backtrace()
30 * directly from their raise() functions may rely on the mask 30 * directly from their raise() functions may rely on the mask
31 * they are passed being updated as a side effect of this call. 31 * they are passed being updated as a side effect of this call.
32 */ 32 */
33void nmi_trigger_all_cpu_backtrace(bool include_self, 33void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
34 bool exclude_self,
34 void (*raise)(cpumask_t *mask)) 35 void (*raise)(cpumask_t *mask))
35{ 36{
36 int i, this_cpu = get_cpu(); 37 int i, this_cpu = get_cpu();
@@ -44,13 +45,13 @@ void nmi_trigger_all_cpu_backtrace(bool include_self,
44 return; 45 return;
45 } 46 }
46 47
47 cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask); 48 cpumask_copy(to_cpumask(backtrace_mask), mask);
48 if (!include_self) 49 if (exclude_self)
49 cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask)); 50 cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask));
50 51
51 if (!cpumask_empty(to_cpumask(backtrace_mask))) { 52 if (!cpumask_empty(to_cpumask(backtrace_mask))) {
52 pr_info("Sending NMI to %s CPUs:\n", 53 pr_info("Sending NMI from CPU %d to CPUs %*pbl:\n",
53 (include_self ? "all" : "other")); 54 this_cpu, nr_cpumask_bits, to_cpumask(backtrace_mask));
54 raise(to_cpumask(backtrace_mask)); 55 raise(to_cpumask(backtrace_mask));
55 } 56 }
56 57