diff options
| -rw-r--r-- | arch/sparc/include/asm/irq_64.h | 4 | ||||
| -rw-r--r-- | arch/sparc/kernel/process_64.c | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/nmi.h | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/apic/nmi.c | 20 | ||||
| -rw-r--r-- | drivers/char/sysrq.c | 19 | ||||
| -rw-r--r-- | include/linux/nmi.h | 19 | ||||
| -rw-r--r-- | kernel/rcutree.c | 7 |
7 files changed, 56 insertions, 21 deletions
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index 1934f2cbf513..a0b443cb3c1f 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h | |||
| @@ -89,8 +89,8 @@ static inline unsigned long get_softint(void) | |||
| 89 | return retval; | 89 | return retval; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | void __trigger_all_cpu_backtrace(void); | 92 | void arch_trigger_all_cpu_backtrace(void); |
| 93 | #define trigger_all_cpu_backtrace() __trigger_all_cpu_backtrace() | 93 | #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace |
| 94 | 94 | ||
| 95 | extern void *hardirq_stack[NR_CPUS]; | 95 | extern void *hardirq_stack[NR_CPUS]; |
| 96 | extern void *softirq_stack[NR_CPUS]; | 96 | extern void *softirq_stack[NR_CPUS]; |
diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index 4041f94e7724..18d67854a1b8 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c | |||
| @@ -251,7 +251,7 @@ static void __global_reg_poll(struct global_reg_snapshot *gp) | |||
| 251 | } | 251 | } |
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | void __trigger_all_cpu_backtrace(void) | 254 | void arch_trigger_all_cpu_backtrace(void) |
| 255 | { | 255 | { |
| 256 | struct thread_info *tp = current_thread_info(); | 256 | struct thread_info *tp = current_thread_info(); |
| 257 | struct pt_regs *regs = get_irq_regs(); | 257 | struct pt_regs *regs = get_irq_regs(); |
| @@ -304,7 +304,7 @@ void __trigger_all_cpu_backtrace(void) | |||
| 304 | 304 | ||
| 305 | static void sysrq_handle_globreg(int key, struct tty_struct *tty) | 305 | static void sysrq_handle_globreg(int key, struct tty_struct *tty) |
| 306 | { | 306 | { |
| 307 | __trigger_all_cpu_backtrace(); | 307 | arch_trigger_all_cpu_backtrace(); |
| 308 | } | 308 | } |
| 309 | 309 | ||
| 310 | static struct sysrq_key_op sparc_globalreg_op = { | 310 | static struct sysrq_key_op sparc_globalreg_op = { |
diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h index c86e5ed4af51..e63cf7d441e1 100644 --- a/arch/x86/include/asm/nmi.h +++ b/arch/x86/include/asm/nmi.h | |||
| @@ -45,8 +45,8 @@ extern int proc_nmi_enabled(struct ctl_table *, int , struct file *, | |||
| 45 | void __user *, size_t *, loff_t *); | 45 | void __user *, size_t *, loff_t *); |
| 46 | extern int unknown_nmi_panic; | 46 | extern int unknown_nmi_panic; |
| 47 | 47 | ||
| 48 | void __trigger_all_cpu_backtrace(void); | 48 | void arch_trigger_all_cpu_backtrace(void); |
| 49 | #define trigger_all_cpu_backtrace() __trigger_all_cpu_backtrace() | 49 | #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace |
| 50 | 50 | ||
| 51 | static inline void localise_nmi_watchdog(void) | 51 | static inline void localise_nmi_watchdog(void) |
| 52 | { | 52 | { |
diff --git a/arch/x86/kernel/apic/nmi.c b/arch/x86/kernel/apic/nmi.c index b3025b43b63a..db7220220d09 100644 --- a/arch/x86/kernel/apic/nmi.c +++ b/arch/x86/kernel/apic/nmi.c | |||
| @@ -39,7 +39,7 @@ | |||
| 39 | int unknown_nmi_panic; | 39 | int unknown_nmi_panic; |
| 40 | int nmi_watchdog_enabled; | 40 | int nmi_watchdog_enabled; |
| 41 | 41 | ||
| 42 | static cpumask_var_t backtrace_mask; | 42 | static cpumask_t backtrace_mask __read_mostly; |
| 43 | 43 | ||
| 44 | /* nmi_active: | 44 | /* nmi_active: |
| 45 | * >0: the lapic NMI watchdog is active, but can be disabled | 45 | * >0: the lapic NMI watchdog is active, but can be disabled |
| @@ -138,7 +138,6 @@ int __init check_nmi_watchdog(void) | |||
| 138 | if (!prev_nmi_count) | 138 | if (!prev_nmi_count) |
| 139 | goto error; | 139 | goto error; |
| 140 | 140 | ||
| 141 | alloc_cpumask_var(&backtrace_mask, GFP_KERNEL|__GFP_ZERO); | ||
| 142 | printk(KERN_INFO "Testing NMI watchdog ... "); | 141 | printk(KERN_INFO "Testing NMI watchdog ... "); |
| 143 | 142 | ||
| 144 | #ifdef CONFIG_SMP | 143 | #ifdef CONFIG_SMP |
| @@ -415,14 +414,17 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason) | |||
| 415 | } | 414 | } |
| 416 | 415 | ||
| 417 | /* We can be called before check_nmi_watchdog, hence NULL check. */ | 416 | /* We can be called before check_nmi_watchdog, hence NULL check. */ |
| 418 | if (backtrace_mask != NULL && cpumask_test_cpu(cpu, backtrace_mask)) { | 417 | if (cpumask_test_cpu(cpu, &backtrace_mask)) { |
| 419 | static DEFINE_SPINLOCK(lock); /* Serialise the printks */ | 418 | static DEFINE_SPINLOCK(lock); /* Serialise the printks */ |
| 420 | 419 | ||
| 421 | spin_lock(&lock); | 420 | spin_lock(&lock); |
| 422 | printk(KERN_WARNING "NMI backtrace for cpu %d\n", cpu); | 421 | printk(KERN_WARNING "NMI backtrace for cpu %d\n", cpu); |
| 422 | show_regs(regs); | ||
| 423 | dump_stack(); | 423 | dump_stack(); |
| 424 | spin_unlock(&lock); | 424 | spin_unlock(&lock); |
| 425 | cpumask_clear_cpu(cpu, backtrace_mask); | 425 | cpumask_clear_cpu(cpu, &backtrace_mask); |
| 426 | |||
| 427 | rc = 1; | ||
| 426 | } | 428 | } |
| 427 | 429 | ||
| 428 | /* Could check oops_in_progress here too, but it's safer not to */ | 430 | /* Could check oops_in_progress here too, but it's safer not to */ |
| @@ -552,14 +554,18 @@ int do_nmi_callback(struct pt_regs *regs, int cpu) | |||
| 552 | return 0; | 554 | return 0; |
| 553 | } | 555 | } |
| 554 | 556 | ||
| 555 | void __trigger_all_cpu_backtrace(void) | 557 | void arch_trigger_all_cpu_backtrace(void) |
| 556 | { | 558 | { |
| 557 | int i; | 559 | int i; |
| 558 | 560 | ||
| 559 | cpumask_copy(backtrace_mask, cpu_online_mask); | 561 | cpumask_copy(&backtrace_mask, cpu_online_mask); |
| 562 | |||
| 563 | printk(KERN_INFO "sending NMI to all CPUs:\n"); | ||
| 564 | apic->send_IPI_all(NMI_VECTOR); | ||
| 565 | |||
| 560 | /* Wait for up to 10 seconds for all CPUs to do the backtrace */ | 566 | /* Wait for up to 10 seconds for all CPUs to do the backtrace */ |
| 561 | for (i = 0; i < 10 * 1000; i++) { | 567 | for (i = 0; i < 10 * 1000; i++) { |
| 562 | if (cpumask_empty(backtrace_mask)) | 568 | if (cpumask_empty(&backtrace_mask)) |
| 563 | break; | 569 | break; |
| 564 | mdelay(1); | 570 | mdelay(1); |
| 565 | } | 571 | } |
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 5d7a02f63e1c..50eecfe1d724 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/sysrq.h> | 24 | #include <linux/sysrq.h> |
| 25 | #include <linux/kbd_kern.h> | 25 | #include <linux/kbd_kern.h> |
| 26 | #include <linux/proc_fs.h> | 26 | #include <linux/proc_fs.h> |
| 27 | #include <linux/nmi.h> | ||
| 27 | #include <linux/quotaops.h> | 28 | #include <linux/quotaops.h> |
| 28 | #include <linux/perf_counter.h> | 29 | #include <linux/perf_counter.h> |
| 29 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
| @@ -222,12 +223,20 @@ static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus); | |||
| 222 | 223 | ||
| 223 | static void sysrq_handle_showallcpus(int key, struct tty_struct *tty) | 224 | static void sysrq_handle_showallcpus(int key, struct tty_struct *tty) |
| 224 | { | 225 | { |
| 225 | struct pt_regs *regs = get_irq_regs(); | 226 | /* |
| 226 | if (regs) { | 227 | * Fall back to the workqueue based printing if the |
| 227 | printk(KERN_INFO "CPU%d:\n", smp_processor_id()); | 228 | * backtrace printing did not succeed or the |
| 228 | show_regs(regs); | 229 | * architecture has no support for it: |
| 230 | */ | ||
| 231 | if (!trigger_all_cpu_backtrace()) { | ||
| 232 | struct pt_regs *regs = get_irq_regs(); | ||
| 233 | |||
| 234 | if (regs) { | ||
| 235 | printk(KERN_INFO "CPU%d:\n", smp_processor_id()); | ||
| 236 | show_regs(regs); | ||
| 237 | } | ||
| 238 | schedule_work(&sysrq_showallcpus); | ||
| 229 | } | 239 | } |
| 230 | schedule_work(&sysrq_showallcpus); | ||
| 231 | } | 240 | } |
| 232 | 241 | ||
| 233 | static struct sysrq_key_op sysrq_showallcpus_op = { | 242 | static struct sysrq_key_op sysrq_showallcpus_op = { |
diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 29af2d5df097..b752e807adde 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h | |||
| @@ -28,8 +28,23 @@ static inline void acpi_nmi_disable(void) { } | |||
| 28 | static inline void acpi_nmi_enable(void) { } | 28 | static inline void acpi_nmi_enable(void) { } |
| 29 | #endif | 29 | #endif |
| 30 | 30 | ||
| 31 | #ifndef trigger_all_cpu_backtrace | 31 | /* |
| 32 | #define trigger_all_cpu_backtrace() do { } while (0) | 32 | * Create trigger_all_cpu_backtrace() out of the arch-provided |
| 33 | * base function. Return whether such support was available, | ||
| 34 | * to allow calling code to fall back to some other mechanism: | ||
| 35 | */ | ||
| 36 | #ifdef arch_trigger_all_cpu_backtrace | ||
| 37 | static inline bool trigger_all_cpu_backtrace(void) | ||
| 38 | { | ||
| 39 | arch_trigger_all_cpu_backtrace(); | ||
| 40 | |||
| 41 | return true; | ||
| 42 | } | ||
| 43 | #else | ||
| 44 | static inline bool trigger_all_cpu_backtrace(void) | ||
| 45 | { | ||
| 46 | return false; | ||
| 47 | } | ||
| 33 | #endif | 48 | #endif |
| 34 | 49 | ||
| 35 | #endif | 50 | #endif |
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 7717b95c2027..9c5fa9fc57ec 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include <linux/rcupdate.h> | 35 | #include <linux/rcupdate.h> |
| 36 | #include <linux/interrupt.h> | 36 | #include <linux/interrupt.h> |
| 37 | #include <linux/sched.h> | 37 | #include <linux/sched.h> |
| 38 | #include <linux/nmi.h> | ||
| 38 | #include <asm/atomic.h> | 39 | #include <asm/atomic.h> |
| 39 | #include <linux/bitops.h> | 40 | #include <linux/bitops.h> |
| 40 | #include <linux/module.h> | 41 | #include <linux/module.h> |
| @@ -469,6 +470,8 @@ static void print_other_cpu_stall(struct rcu_state *rsp) | |||
| 469 | } | 470 | } |
| 470 | printk(" (detected by %d, t=%ld jiffies)\n", | 471 | printk(" (detected by %d, t=%ld jiffies)\n", |
| 471 | smp_processor_id(), (long)(jiffies - rsp->gp_start)); | 472 | smp_processor_id(), (long)(jiffies - rsp->gp_start)); |
| 473 | trigger_all_cpu_backtrace(); | ||
| 474 | |||
| 472 | force_quiescent_state(rsp, 0); /* Kick them all. */ | 475 | force_quiescent_state(rsp, 0); /* Kick them all. */ |
| 473 | } | 476 | } |
| 474 | 477 | ||
| @@ -479,12 +482,14 @@ static void print_cpu_stall(struct rcu_state *rsp) | |||
| 479 | 482 | ||
| 480 | printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu jiffies)\n", | 483 | printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu jiffies)\n", |
| 481 | smp_processor_id(), jiffies - rsp->gp_start); | 484 | smp_processor_id(), jiffies - rsp->gp_start); |
| 482 | dump_stack(); | 485 | trigger_all_cpu_backtrace(); |
| 486 | |||
| 483 | spin_lock_irqsave(&rnp->lock, flags); | 487 | spin_lock_irqsave(&rnp->lock, flags); |
| 484 | if ((long)(jiffies - rsp->jiffies_stall) >= 0) | 488 | if ((long)(jiffies - rsp->jiffies_stall) >= 0) |
| 485 | rsp->jiffies_stall = | 489 | rsp->jiffies_stall = |
| 486 | jiffies + RCU_SECONDS_TILL_STALL_RECHECK; | 490 | jiffies + RCU_SECONDS_TILL_STALL_RECHECK; |
| 487 | spin_unlock_irqrestore(&rnp->lock, flags); | 491 | spin_unlock_irqrestore(&rnp->lock, flags); |
| 492 | |||
| 488 | set_need_resched(); /* kick ourselves to get things going. */ | 493 | set_need_resched(); /* kick ourselves to get things going. */ |
| 489 | } | 494 | } |
| 490 | 495 | ||
