diff options
-rw-r--r-- | arch/arm/include/asm/irq.h | 5 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 4 | ||||
-rw-r--r-- | arch/mips/include/asm/irq.h | 5 | ||||
-rw-r--r-- | arch/mips/kernel/process.c | 11 | ||||
-rw-r--r-- | arch/sparc/include/asm/irq_64.h | 5 | ||||
-rw-r--r-- | arch/sparc/kernel/process_64.c | 10 | ||||
-rw-r--r-- | arch/x86/include/asm/irq.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/apic/hw_nmi.c | 18 | ||||
-rw-r--r-- | include/linux/nmi.h | 31 | ||||
-rw-r--r-- | lib/nmi_backtrace.c | 17 |
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 |
39 | extern void arch_trigger_all_cpu_backtrace(bool); | 39 | extern 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 | ||
43 | static inline int nr_legacy_irqs(void) | 44 | static 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 | ||
763 | void arch_trigger_all_cpu_backtrace(bool include_self) | 763 | void 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 | ||
52 | extern int get_c0_fdc_int(void); | 52 | extern int get_c0_fdc_int(void); |
53 | 53 | ||
54 | void arch_trigger_all_cpu_backtrace(bool); | 54 | void 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 | ||
572 | void arch_trigger_all_cpu_backtrace(bool include_self) | 572 | void 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 | ||
577 | int mips_get_process_fp_mode(struct task_struct *task) | 584 | int 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 | ||
89 | void arch_trigger_all_cpu_backtrace(bool); | 89 | void 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 | ||
92 | extern void *hardirq_stack[NR_CPUS]; | 93 | extern void *hardirq_stack[NR_CPUS]; |
93 | extern void *softirq_stack[NR_CPUS]; | 94 | extern 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 | ||
242 | void arch_trigger_all_cpu_backtrace(bool include_self) | 242 | void 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 | ||
301 | static void sysrq_handle_globreg(int key) | 301 | static void sysrq_handle_globreg(int key) |
302 | { | 302 | { |
303 | arch_trigger_all_cpu_backtrace(true); | 303 | trigger_all_cpu_backtrace(); |
304 | } | 304 | } |
305 | 305 | ||
306 | static struct sysrq_key_op sparc_globalreg_op = { | 306 | static 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); | |||
50 | extern void init_ISA_irqs(void); | 50 | extern void init_ISA_irqs(void); |
51 | 51 | ||
52 | #ifdef CONFIG_X86_LOCAL_APIC | 52 | #ifdef CONFIG_X86_LOCAL_APIC |
53 | void arch_trigger_all_cpu_backtrace(bool); | 53 | void 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 |
30 | static void nmi_raise_cpu_backtrace(cpumask_t *mask) | 30 | static 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 | ||
35 | void arch_trigger_all_cpu_backtrace(bool include_self) | 35 | void 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 | ||
40 | static int | 41 | static int nmi_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs) |
41 | arch_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 | } |
48 | NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler); | 48 | NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); |
49 | 49 | ||
50 | static int __init register_trigger_all_cpu_backtrace(void) | 50 | static 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 | } |
56 | early_initcall(register_trigger_all_cpu_backtrace); | 56 | early_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 |
39 | static inline bool trigger_all_cpu_backtrace(void) | 39 | static 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 | |||
45 | static inline bool trigger_allbutself_cpu_backtrace(void) | 45 | static 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 | |||
51 | static inline bool trigger_cpumask_backtrace(struct cpumask *mask) | ||
52 | { | ||
53 | arch_trigger_cpumask_backtrace(mask, false); | ||
54 | return true; | ||
55 | } | ||
56 | |||
57 | static 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 */ |
52 | void nmi_trigger_all_cpu_backtrace(bool include_self, | 64 | void nmi_trigger_cpumask_backtrace(const cpumask_t *mask, |
65 | bool exclude_self, | ||
53 | void (*raise)(cpumask_t *mask)); | 66 | void (*raise)(cpumask_t *mask)); |
54 | bool nmi_cpu_backtrace(struct pt_regs *regs); | 67 | bool 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 | } |
78 | static inline bool trigger_cpumask_backtrace(struct cpumask *mask) | ||
79 | { | ||
80 | return false; | ||
81 | } | ||
82 | static 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. */ |
22 | static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; | 22 | static 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 */ |
25 | static unsigned long backtrace_flag; | 25 | static 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 | */ |
33 | void nmi_trigger_all_cpu_backtrace(bool include_self, | 33 | void 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 | ||