diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-11 22:01:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-11 22:01:14 -0500 |
commit | 9b66bfb28049594fe2bb2b91607ba302f511ce8b (patch) | |
tree | e96e79b1864699800c3f2c2e06b482a995744daf /arch | |
parent | c2136301e43cbb3b71d0163a9949f30dafcb4590 (diff) | |
parent | b5dfcb09debc38582c3cb72a3c1a88b919b07f2d (diff) |
Merge branch 'x86-uv-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 UV debug changes from Ingo Molnar:
"Various SGI UV debuggability improvements, amongst them KDB support,
with related core KDB enabling patches changing kernel/debug/kdb/"
* 'x86-uv-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
Revert "x86/UV: Add uvtrace support"
x86/UV: Add call to KGDB/KDB from NMI handler
kdb: Add support for external NMI handler to call KGDB/KDB
x86/UV: Check for alloc_cpumask_var() failures properly in uv_nmi_setup()
x86/UV: Add uvtrace support
x86/UV: Add kdump to UV NMI handler
x86/UV: Add summary of cpu activity to UV NMI handler
x86/UV: Update UV support for external NMI signals
x86/UV: Move NMI support
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/uv/uv.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/uv/uv_hub.h | 57 | ||||
-rw-r--r-- | arch/x86/include/asm/uv/uv_mmrs.h | 31 | ||||
-rw-r--r-- | arch/x86/kernel/apic/x2apic_uv_x.c | 70 | ||||
-rw-r--r-- | arch/x86/platform/uv/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/platform/uv/uv_nmi.c | 700 |
6 files changed, 790 insertions, 72 deletions
diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 062921ef34e9..6b964a0b86d1 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h | |||
@@ -12,6 +12,7 @@ extern enum uv_system_type get_uv_system_type(void); | |||
12 | extern int is_uv_system(void); | 12 | extern int is_uv_system(void); |
13 | extern void uv_cpu_init(void); | 13 | extern void uv_cpu_init(void); |
14 | extern void uv_nmi_init(void); | 14 | extern void uv_nmi_init(void); |
15 | extern void uv_register_nmi_notifier(void); | ||
15 | extern void uv_system_init(void); | 16 | extern void uv_system_init(void); |
16 | extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, | 17 | extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, |
17 | struct mm_struct *mm, | 18 | struct mm_struct *mm, |
@@ -25,6 +26,7 @@ static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; } | |||
25 | static inline int is_uv_system(void) { return 0; } | 26 | static inline int is_uv_system(void) { return 0; } |
26 | static inline void uv_cpu_init(void) { } | 27 | static inline void uv_cpu_init(void) { } |
27 | static inline void uv_system_init(void) { } | 28 | static inline void uv_system_init(void) { } |
29 | static inline void uv_register_nmi_notifier(void) { } | ||
28 | static inline const struct cpumask * | 30 | static inline const struct cpumask * |
29 | uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, | 31 | uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, |
30 | unsigned long start, unsigned long end, unsigned int cpu) | 32 | unsigned long start, unsigned long end, unsigned int cpu) |
diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index 2c32df95bb78..a30836c8ac4d 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h | |||
@@ -502,8 +502,8 @@ struct uv_blade_info { | |||
502 | unsigned short nr_online_cpus; | 502 | unsigned short nr_online_cpus; |
503 | unsigned short pnode; | 503 | unsigned short pnode; |
504 | short memory_nid; | 504 | short memory_nid; |
505 | spinlock_t nmi_lock; | 505 | spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ |
506 | unsigned long nmi_count; | 506 | unsigned long nmi_count; /* obsolete, see uv_hub_nmi */ |
507 | }; | 507 | }; |
508 | extern struct uv_blade_info *uv_blade_info; | 508 | extern struct uv_blade_info *uv_blade_info; |
509 | extern short *uv_node_to_blade; | 509 | extern short *uv_node_to_blade; |
@@ -576,6 +576,59 @@ static inline int uv_num_possible_blades(void) | |||
576 | return uv_possible_blades; | 576 | return uv_possible_blades; |
577 | } | 577 | } |
578 | 578 | ||
579 | /* Per Hub NMI support */ | ||
580 | extern void uv_nmi_setup(void); | ||
581 | |||
582 | /* BMC sets a bit this MMR non-zero before sending an NMI */ | ||
583 | #define UVH_NMI_MMR UVH_SCRATCH5 | ||
584 | #define UVH_NMI_MMR_CLEAR UVH_SCRATCH5_ALIAS | ||
585 | #define UVH_NMI_MMR_SHIFT 63 | ||
586 | #define UVH_NMI_MMR_TYPE "SCRATCH5" | ||
587 | |||
588 | /* Newer SMM NMI handler, not present in all systems */ | ||
589 | #define UVH_NMI_MMRX UVH_EVENT_OCCURRED0 | ||
590 | #define UVH_NMI_MMRX_CLEAR UVH_EVENT_OCCURRED0_ALIAS | ||
591 | #define UVH_NMI_MMRX_SHIFT (is_uv1_hub() ? \ | ||
592 | UV1H_EVENT_OCCURRED0_EXTIO_INT0_SHFT :\ | ||
593 | UVXH_EVENT_OCCURRED0_EXTIO_INT0_SHFT) | ||
594 | #define UVH_NMI_MMRX_TYPE "EXTIO_INT0" | ||
595 | |||
596 | /* Non-zero indicates newer SMM NMI handler present */ | ||
597 | #define UVH_NMI_MMRX_SUPPORTED UVH_EXTIO_INT0_BROADCAST | ||
598 | |||
599 | /* Indicates to BIOS that we want to use the newer SMM NMI handler */ | ||
600 | #define UVH_NMI_MMRX_REQ UVH_SCRATCH5_ALIAS_2 | ||
601 | #define UVH_NMI_MMRX_REQ_SHIFT 62 | ||
602 | |||
603 | struct uv_hub_nmi_s { | ||
604 | raw_spinlock_t nmi_lock; | ||
605 | atomic_t in_nmi; /* flag this node in UV NMI IRQ */ | ||
606 | atomic_t cpu_owner; /* last locker of this struct */ | ||
607 | atomic_t read_mmr_count; /* count of MMR reads */ | ||
608 | atomic_t nmi_count; /* count of true UV NMIs */ | ||
609 | unsigned long nmi_value; /* last value read from NMI MMR */ | ||
610 | }; | ||
611 | |||
612 | struct uv_cpu_nmi_s { | ||
613 | struct uv_hub_nmi_s *hub; | ||
614 | atomic_t state; | ||
615 | atomic_t pinging; | ||
616 | int queries; | ||
617 | int pings; | ||
618 | }; | ||
619 | |||
620 | DECLARE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); | ||
621 | #define uv_cpu_nmi (__get_cpu_var(__uv_cpu_nmi)) | ||
622 | #define uv_hub_nmi (uv_cpu_nmi.hub) | ||
623 | #define uv_cpu_nmi_per(cpu) (per_cpu(__uv_cpu_nmi, cpu)) | ||
624 | #define uv_hub_nmi_per(cpu) (uv_cpu_nmi_per(cpu).hub) | ||
625 | |||
626 | /* uv_cpu_nmi_states */ | ||
627 | #define UV_NMI_STATE_OUT 0 | ||
628 | #define UV_NMI_STATE_IN 1 | ||
629 | #define UV_NMI_STATE_DUMP 2 | ||
630 | #define UV_NMI_STATE_DUMP_DONE 3 | ||
631 | |||
579 | /* Update SCIR state */ | 632 | /* Update SCIR state */ |
580 | static inline void uv_set_scir_bits(unsigned char value) | 633 | static inline void uv_set_scir_bits(unsigned char value) |
581 | { | 634 | { |
diff --git a/arch/x86/include/asm/uv/uv_mmrs.h b/arch/x86/include/asm/uv/uv_mmrs.h index bd5f80e58a23..e42249bcf7e1 100644 --- a/arch/x86/include/asm/uv/uv_mmrs.h +++ b/arch/x86/include/asm/uv/uv_mmrs.h | |||
@@ -461,6 +461,23 @@ union uvh_event_occurred0_u { | |||
461 | 461 | ||
462 | 462 | ||
463 | /* ========================================================================= */ | 463 | /* ========================================================================= */ |
464 | /* UVH_EXTIO_INT0_BROADCAST */ | ||
465 | /* ========================================================================= */ | ||
466 | #define UVH_EXTIO_INT0_BROADCAST 0x61448UL | ||
467 | #define UVH_EXTIO_INT0_BROADCAST_32 0x3f0 | ||
468 | |||
469 | #define UVH_EXTIO_INT0_BROADCAST_ENABLE_SHFT 0 | ||
470 | #define UVH_EXTIO_INT0_BROADCAST_ENABLE_MASK 0x0000000000000001UL | ||
471 | |||
472 | union uvh_extio_int0_broadcast_u { | ||
473 | unsigned long v; | ||
474 | struct uvh_extio_int0_broadcast_s { | ||
475 | unsigned long enable:1; /* RW */ | ||
476 | unsigned long rsvd_1_63:63; | ||
477 | } s; | ||
478 | }; | ||
479 | |||
480 | /* ========================================================================= */ | ||
464 | /* UVH_GR0_TLB_INT0_CONFIG */ | 481 | /* UVH_GR0_TLB_INT0_CONFIG */ |
465 | /* ========================================================================= */ | 482 | /* ========================================================================= */ |
466 | #define UVH_GR0_TLB_INT0_CONFIG 0x61b00UL | 483 | #define UVH_GR0_TLB_INT0_CONFIG 0x61b00UL |
@@ -2606,6 +2623,20 @@ union uvh_scratch5_u { | |||
2606 | }; | 2623 | }; |
2607 | 2624 | ||
2608 | /* ========================================================================= */ | 2625 | /* ========================================================================= */ |
2626 | /* UVH_SCRATCH5_ALIAS */ | ||
2627 | /* ========================================================================= */ | ||
2628 | #define UVH_SCRATCH5_ALIAS 0x2d0208UL | ||
2629 | #define UVH_SCRATCH5_ALIAS_32 0x780 | ||
2630 | |||
2631 | |||
2632 | /* ========================================================================= */ | ||
2633 | /* UVH_SCRATCH5_ALIAS_2 */ | ||
2634 | /* ========================================================================= */ | ||
2635 | #define UVH_SCRATCH5_ALIAS_2 0x2d0210UL | ||
2636 | #define UVH_SCRATCH5_ALIAS_2_32 0x788 | ||
2637 | |||
2638 | |||
2639 | /* ========================================================================= */ | ||
2609 | /* UVXH_EVENT_OCCURRED2 */ | 2640 | /* UVXH_EVENT_OCCURRED2 */ |
2610 | /* ========================================================================= */ | 2641 | /* ========================================================================= */ |
2611 | #define UVXH_EVENT_OCCURRED2 0x70100UL | 2642 | #define UVXH_EVENT_OCCURRED2 0x70100UL |
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index a419814cea57..ad0dc0428baf 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c | |||
@@ -39,12 +39,6 @@ | |||
39 | #include <asm/x86_init.h> | 39 | #include <asm/x86_init.h> |
40 | #include <asm/nmi.h> | 40 | #include <asm/nmi.h> |
41 | 41 | ||
42 | /* BMC sets a bit this MMR non-zero before sending an NMI */ | ||
43 | #define UVH_NMI_MMR UVH_SCRATCH5 | ||
44 | #define UVH_NMI_MMR_CLEAR (UVH_NMI_MMR + 8) | ||
45 | #define UV_NMI_PENDING_MASK (1UL << 63) | ||
46 | DEFINE_PER_CPU(unsigned long, cpu_last_nmi_count); | ||
47 | |||
48 | DEFINE_PER_CPU(int, x2apic_extra_bits); | 42 | DEFINE_PER_CPU(int, x2apic_extra_bits); |
49 | 43 | ||
50 | #define PR_DEVEL(fmt, args...) pr_devel("%s: " fmt, __func__, args) | 44 | #define PR_DEVEL(fmt, args...) pr_devel("%s: " fmt, __func__, args) |
@@ -58,7 +52,6 @@ int uv_min_hub_revision_id; | |||
58 | EXPORT_SYMBOL_GPL(uv_min_hub_revision_id); | 52 | EXPORT_SYMBOL_GPL(uv_min_hub_revision_id); |
59 | unsigned int uv_apicid_hibits; | 53 | unsigned int uv_apicid_hibits; |
60 | EXPORT_SYMBOL_GPL(uv_apicid_hibits); | 54 | EXPORT_SYMBOL_GPL(uv_apicid_hibits); |
61 | static DEFINE_SPINLOCK(uv_nmi_lock); | ||
62 | 55 | ||
63 | static struct apic apic_x2apic_uv_x; | 56 | static struct apic apic_x2apic_uv_x; |
64 | 57 | ||
@@ -847,68 +840,6 @@ void uv_cpu_init(void) | |||
847 | set_x2apic_extra_bits(uv_hub_info->pnode); | 840 | set_x2apic_extra_bits(uv_hub_info->pnode); |
848 | } | 841 | } |
849 | 842 | ||
850 | /* | ||
851 | * When NMI is received, print a stack trace. | ||
852 | */ | ||
853 | int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) | ||
854 | { | ||
855 | unsigned long real_uv_nmi; | ||
856 | int bid; | ||
857 | |||
858 | /* | ||
859 | * Each blade has an MMR that indicates when an NMI has been sent | ||
860 | * to cpus on the blade. If an NMI is detected, atomically | ||
861 | * clear the MMR and update a per-blade NMI count used to | ||
862 | * cause each cpu on the blade to notice a new NMI. | ||
863 | */ | ||
864 | bid = uv_numa_blade_id(); | ||
865 | real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); | ||
866 | |||
867 | if (unlikely(real_uv_nmi)) { | ||
868 | spin_lock(&uv_blade_info[bid].nmi_lock); | ||
869 | real_uv_nmi = (uv_read_local_mmr(UVH_NMI_MMR) & UV_NMI_PENDING_MASK); | ||
870 | if (real_uv_nmi) { | ||
871 | uv_blade_info[bid].nmi_count++; | ||
872 | uv_write_local_mmr(UVH_NMI_MMR_CLEAR, UV_NMI_PENDING_MASK); | ||
873 | } | ||
874 | spin_unlock(&uv_blade_info[bid].nmi_lock); | ||
875 | } | ||
876 | |||
877 | if (likely(__get_cpu_var(cpu_last_nmi_count) == uv_blade_info[bid].nmi_count)) | ||
878 | return NMI_DONE; | ||
879 | |||
880 | __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; | ||
881 | |||
882 | /* | ||
883 | * Use a lock so only one cpu prints at a time. | ||
884 | * This prevents intermixed output. | ||
885 | */ | ||
886 | spin_lock(&uv_nmi_lock); | ||
887 | pr_info("UV NMI stack dump cpu %u:\n", smp_processor_id()); | ||
888 | dump_stack(); | ||
889 | spin_unlock(&uv_nmi_lock); | ||
890 | |||
891 | return NMI_HANDLED; | ||
892 | } | ||
893 | |||
894 | void uv_register_nmi_notifier(void) | ||
895 | { | ||
896 | if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) | ||
897 | printk(KERN_WARNING "UV NMI handler failed to register\n"); | ||
898 | } | ||
899 | |||
900 | void uv_nmi_init(void) | ||
901 | { | ||
902 | unsigned int value; | ||
903 | |||
904 | /* | ||
905 | * Unmask NMI on all cpus | ||
906 | */ | ||
907 | value = apic_read(APIC_LVT1) | APIC_DM_NMI; | ||
908 | value &= ~APIC_LVT_MASKED; | ||
909 | apic_write(APIC_LVT1, value); | ||
910 | } | ||
911 | |||
912 | void __init uv_system_init(void) | 843 | void __init uv_system_init(void) |
913 | { | 844 | { |
914 | union uvh_rh_gam_config_mmr_u m_n_config; | 845 | union uvh_rh_gam_config_mmr_u m_n_config; |
@@ -1046,6 +977,7 @@ void __init uv_system_init(void) | |||
1046 | map_mmr_high(max_pnode); | 977 | map_mmr_high(max_pnode); |
1047 | map_mmioh_high(min_pnode, max_pnode); | 978 | map_mmioh_high(min_pnode, max_pnode); |
1048 | 979 | ||
980 | uv_nmi_setup(); | ||
1049 | uv_cpu_init(); | 981 | uv_cpu_init(); |
1050 | uv_scir_register_cpu_notifier(); | 982 | uv_scir_register_cpu_notifier(); |
1051 | uv_register_nmi_notifier(); | 983 | uv_register_nmi_notifier(); |
diff --git a/arch/x86/platform/uv/Makefile b/arch/x86/platform/uv/Makefile index 6c40995fefb8..52079bebd014 100644 --- a/arch/x86/platform/uv/Makefile +++ b/arch/x86/platform/uv/Makefile | |||
@@ -1 +1 @@ | |||
obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o | obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o | ||
diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c new file mode 100644 index 000000000000..2e863ad4a772 --- /dev/null +++ b/arch/x86/platform/uv/uv_nmi.c | |||
@@ -0,0 +1,700 @@ | |||
1 | /* | ||
2 | * SGI NMI support routines | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | * | ||
18 | * Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved. | ||
19 | * Copyright (c) Mike Travis | ||
20 | */ | ||
21 | |||
22 | #include <linux/cpu.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/kdb.h> | ||
25 | #include <linux/kexec.h> | ||
26 | #include <linux/kgdb.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/nmi.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/slab.h> | ||
31 | |||
32 | #include <asm/apic.h> | ||
33 | #include <asm/current.h> | ||
34 | #include <asm/kdebug.h> | ||
35 | #include <asm/local64.h> | ||
36 | #include <asm/nmi.h> | ||
37 | #include <asm/traps.h> | ||
38 | #include <asm/uv/uv.h> | ||
39 | #include <asm/uv/uv_hub.h> | ||
40 | #include <asm/uv/uv_mmrs.h> | ||
41 | |||
42 | /* | ||
43 | * UV handler for NMI | ||
44 | * | ||
45 | * Handle system-wide NMI events generated by the global 'power nmi' command. | ||
46 | * | ||
47 | * Basic operation is to field the NMI interrupt on each cpu and wait | ||
48 | * until all cpus have arrived into the nmi handler. If some cpus do not | ||
49 | * make it into the handler, try and force them in with the IPI(NMI) signal. | ||
50 | * | ||
51 | * We also have to lessen UV Hub MMR accesses as much as possible as this | ||
52 | * disrupts the UV Hub's primary mission of directing NumaLink traffic and | ||
53 | * can cause system problems to occur. | ||
54 | * | ||
55 | * To do this we register our primary NMI notifier on the NMI_UNKNOWN | ||
56 | * chain. This reduces the number of false NMI calls when the perf | ||
57 | * tools are running which generate an enormous number of NMIs per | ||
58 | * second (~4M/s for 1024 cpu threads). Our secondary NMI handler is | ||
59 | * very short as it only checks that if it has been "pinged" with the | ||
60 | * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR. | ||
61 | * | ||
62 | */ | ||
63 | |||
64 | static struct uv_hub_nmi_s **uv_hub_nmi_list; | ||
65 | |||
66 | DEFINE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); | ||
67 | EXPORT_PER_CPU_SYMBOL_GPL(__uv_cpu_nmi); | ||
68 | |||
69 | static unsigned long nmi_mmr; | ||
70 | static unsigned long nmi_mmr_clear; | ||
71 | static unsigned long nmi_mmr_pending; | ||
72 | |||
73 | static atomic_t uv_in_nmi; | ||
74 | static atomic_t uv_nmi_cpu = ATOMIC_INIT(-1); | ||
75 | static atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1); | ||
76 | static atomic_t uv_nmi_slave_continue; | ||
77 | static atomic_t uv_nmi_kexec_failed; | ||
78 | static cpumask_var_t uv_nmi_cpu_mask; | ||
79 | |||
80 | /* Values for uv_nmi_slave_continue */ | ||
81 | #define SLAVE_CLEAR 0 | ||
82 | #define SLAVE_CONTINUE 1 | ||
83 | #define SLAVE_EXIT 2 | ||
84 | |||
85 | /* | ||
86 | * Default is all stack dumps go to the console and buffer. | ||
87 | * Lower level to send to log buffer only. | ||
88 | */ | ||
89 | static int uv_nmi_loglevel = 7; | ||
90 | module_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644); | ||
91 | |||
92 | /* | ||
93 | * The following values show statistics on how perf events are affecting | ||
94 | * this system. | ||
95 | */ | ||
96 | static int param_get_local64(char *buffer, const struct kernel_param *kp) | ||
97 | { | ||
98 | return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg)); | ||
99 | } | ||
100 | |||
101 | static int param_set_local64(const char *val, const struct kernel_param *kp) | ||
102 | { | ||
103 | /* clear on any write */ | ||
104 | local64_set((local64_t *)kp->arg, 0); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static struct kernel_param_ops param_ops_local64 = { | ||
109 | .get = param_get_local64, | ||
110 | .set = param_set_local64, | ||
111 | }; | ||
112 | #define param_check_local64(name, p) __param_check(name, p, local64_t) | ||
113 | |||
114 | static local64_t uv_nmi_count; | ||
115 | module_param_named(nmi_count, uv_nmi_count, local64, 0644); | ||
116 | |||
117 | static local64_t uv_nmi_misses; | ||
118 | module_param_named(nmi_misses, uv_nmi_misses, local64, 0644); | ||
119 | |||
120 | static local64_t uv_nmi_ping_count; | ||
121 | module_param_named(ping_count, uv_nmi_ping_count, local64, 0644); | ||
122 | |||
123 | static local64_t uv_nmi_ping_misses; | ||
124 | module_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644); | ||
125 | |||
126 | /* | ||
127 | * Following values allow tuning for large systems under heavy loading | ||
128 | */ | ||
129 | static int uv_nmi_initial_delay = 100; | ||
130 | module_param_named(initial_delay, uv_nmi_initial_delay, int, 0644); | ||
131 | |||
132 | static int uv_nmi_slave_delay = 100; | ||
133 | module_param_named(slave_delay, uv_nmi_slave_delay, int, 0644); | ||
134 | |||
135 | static int uv_nmi_loop_delay = 100; | ||
136 | module_param_named(loop_delay, uv_nmi_loop_delay, int, 0644); | ||
137 | |||
138 | static int uv_nmi_trigger_delay = 10000; | ||
139 | module_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644); | ||
140 | |||
141 | static int uv_nmi_wait_count = 100; | ||
142 | module_param_named(wait_count, uv_nmi_wait_count, int, 0644); | ||
143 | |||
144 | static int uv_nmi_retry_count = 500; | ||
145 | module_param_named(retry_count, uv_nmi_retry_count, int, 0644); | ||
146 | |||
147 | /* | ||
148 | * Valid NMI Actions: | ||
149 | * "dump" - dump process stack for each cpu | ||
150 | * "ips" - dump IP info for each cpu | ||
151 | * "kdump" - do crash dump | ||
152 | * "kdb" - enter KDB/KGDB (default) | ||
153 | */ | ||
154 | static char uv_nmi_action[8] = "kdb"; | ||
155 | module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644); | ||
156 | |||
157 | static inline bool uv_nmi_action_is(const char *action) | ||
158 | { | ||
159 | return (strncmp(uv_nmi_action, action, strlen(action)) == 0); | ||
160 | } | ||
161 | |||
162 | /* Setup which NMI support is present in system */ | ||
163 | static void uv_nmi_setup_mmrs(void) | ||
164 | { | ||
165 | if (uv_read_local_mmr(UVH_NMI_MMRX_SUPPORTED)) { | ||
166 | uv_write_local_mmr(UVH_NMI_MMRX_REQ, | ||
167 | 1UL << UVH_NMI_MMRX_REQ_SHIFT); | ||
168 | nmi_mmr = UVH_NMI_MMRX; | ||
169 | nmi_mmr_clear = UVH_NMI_MMRX_CLEAR; | ||
170 | nmi_mmr_pending = 1UL << UVH_NMI_MMRX_SHIFT; | ||
171 | pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMRX_TYPE); | ||
172 | } else { | ||
173 | nmi_mmr = UVH_NMI_MMR; | ||
174 | nmi_mmr_clear = UVH_NMI_MMR_CLEAR; | ||
175 | nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT; | ||
176 | pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /* Read NMI MMR and check if NMI flag was set by BMC. */ | ||
181 | static inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi) | ||
182 | { | ||
183 | hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr); | ||
184 | atomic_inc(&hub_nmi->read_mmr_count); | ||
185 | return !!(hub_nmi->nmi_value & nmi_mmr_pending); | ||
186 | } | ||
187 | |||
188 | static inline void uv_local_mmr_clear_nmi(void) | ||
189 | { | ||
190 | uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending); | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * If first cpu in on this hub, set hub_nmi "in_nmi" and "owner" values and | ||
195 | * return true. If first cpu in on the system, set global "in_nmi" flag. | ||
196 | */ | ||
197 | static int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi) | ||
198 | { | ||
199 | int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1); | ||
200 | |||
201 | if (first) { | ||
202 | atomic_set(&hub_nmi->cpu_owner, cpu); | ||
203 | if (atomic_add_unless(&uv_in_nmi, 1, 1)) | ||
204 | atomic_set(&uv_nmi_cpu, cpu); | ||
205 | |||
206 | atomic_inc(&hub_nmi->nmi_count); | ||
207 | } | ||
208 | return first; | ||
209 | } | ||
210 | |||
211 | /* Check if this is a system NMI event */ | ||
212 | static int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi) | ||
213 | { | ||
214 | int cpu = smp_processor_id(); | ||
215 | int nmi = 0; | ||
216 | |||
217 | local64_inc(&uv_nmi_count); | ||
218 | uv_cpu_nmi.queries++; | ||
219 | |||
220 | do { | ||
221 | nmi = atomic_read(&hub_nmi->in_nmi); | ||
222 | if (nmi) | ||
223 | break; | ||
224 | |||
225 | if (raw_spin_trylock(&hub_nmi->nmi_lock)) { | ||
226 | |||
227 | /* check hub MMR NMI flag */ | ||
228 | if (uv_nmi_test_mmr(hub_nmi)) { | ||
229 | uv_set_in_nmi(cpu, hub_nmi); | ||
230 | nmi = 1; | ||
231 | break; | ||
232 | } | ||
233 | |||
234 | /* MMR NMI flag is clear */ | ||
235 | raw_spin_unlock(&hub_nmi->nmi_lock); | ||
236 | |||
237 | } else { | ||
238 | /* wait a moment for the hub nmi locker to set flag */ | ||
239 | cpu_relax(); | ||
240 | udelay(uv_nmi_slave_delay); | ||
241 | |||
242 | /* re-check hub in_nmi flag */ | ||
243 | nmi = atomic_read(&hub_nmi->in_nmi); | ||
244 | if (nmi) | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | /* check if this BMC missed setting the MMR NMI flag */ | ||
249 | if (!nmi) { | ||
250 | nmi = atomic_read(&uv_in_nmi); | ||
251 | if (nmi) | ||
252 | uv_set_in_nmi(cpu, hub_nmi); | ||
253 | } | ||
254 | |||
255 | } while (0); | ||
256 | |||
257 | if (!nmi) | ||
258 | local64_inc(&uv_nmi_misses); | ||
259 | |||
260 | return nmi; | ||
261 | } | ||
262 | |||
263 | /* Need to reset the NMI MMR register, but only once per hub. */ | ||
264 | static inline void uv_clear_nmi(int cpu) | ||
265 | { | ||
266 | struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; | ||
267 | |||
268 | if (cpu == atomic_read(&hub_nmi->cpu_owner)) { | ||
269 | atomic_set(&hub_nmi->cpu_owner, -1); | ||
270 | atomic_set(&hub_nmi->in_nmi, 0); | ||
271 | uv_local_mmr_clear_nmi(); | ||
272 | raw_spin_unlock(&hub_nmi->nmi_lock); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /* Print non-responding cpus */ | ||
277 | static void uv_nmi_nr_cpus_pr(char *fmt) | ||
278 | { | ||
279 | static char cpu_list[1024]; | ||
280 | int len = sizeof(cpu_list); | ||
281 | int c = cpumask_weight(uv_nmi_cpu_mask); | ||
282 | int n = cpulist_scnprintf(cpu_list, len, uv_nmi_cpu_mask); | ||
283 | |||
284 | if (n >= len-1) | ||
285 | strcpy(&cpu_list[len - 6], "...\n"); | ||
286 | |||
287 | printk(fmt, c, cpu_list); | ||
288 | } | ||
289 | |||
290 | /* Ping non-responding cpus attemping to force them into the NMI handler */ | ||
291 | static void uv_nmi_nr_cpus_ping(void) | ||
292 | { | ||
293 | int cpu; | ||
294 | |||
295 | for_each_cpu(cpu, uv_nmi_cpu_mask) | ||
296 | atomic_set(&uv_cpu_nmi_per(cpu).pinging, 1); | ||
297 | |||
298 | apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI); | ||
299 | } | ||
300 | |||
301 | /* Clean up flags for cpus that ignored both NMI and ping */ | ||
302 | static void uv_nmi_cleanup_mask(void) | ||
303 | { | ||
304 | int cpu; | ||
305 | |||
306 | for_each_cpu(cpu, uv_nmi_cpu_mask) { | ||
307 | atomic_set(&uv_cpu_nmi_per(cpu).pinging, 0); | ||
308 | atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_OUT); | ||
309 | cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | /* Loop waiting as cpus enter nmi handler */ | ||
314 | static int uv_nmi_wait_cpus(int first) | ||
315 | { | ||
316 | int i, j, k, n = num_online_cpus(); | ||
317 | int last_k = 0, waiting = 0; | ||
318 | |||
319 | if (first) { | ||
320 | cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask); | ||
321 | k = 0; | ||
322 | } else { | ||
323 | k = n - cpumask_weight(uv_nmi_cpu_mask); | ||
324 | } | ||
325 | |||
326 | udelay(uv_nmi_initial_delay); | ||
327 | for (i = 0; i < uv_nmi_retry_count; i++) { | ||
328 | int loop_delay = uv_nmi_loop_delay; | ||
329 | |||
330 | for_each_cpu(j, uv_nmi_cpu_mask) { | ||
331 | if (atomic_read(&uv_cpu_nmi_per(j).state)) { | ||
332 | cpumask_clear_cpu(j, uv_nmi_cpu_mask); | ||
333 | if (++k >= n) | ||
334 | break; | ||
335 | } | ||
336 | } | ||
337 | if (k >= n) { /* all in? */ | ||
338 | k = n; | ||
339 | break; | ||
340 | } | ||
341 | if (last_k != k) { /* abort if no new cpus coming in */ | ||
342 | last_k = k; | ||
343 | waiting = 0; | ||
344 | } else if (++waiting > uv_nmi_wait_count) | ||
345 | break; | ||
346 | |||
347 | /* extend delay if waiting only for cpu 0 */ | ||
348 | if (waiting && (n - k) == 1 && | ||
349 | cpumask_test_cpu(0, uv_nmi_cpu_mask)) | ||
350 | loop_delay *= 100; | ||
351 | |||
352 | udelay(loop_delay); | ||
353 | } | ||
354 | atomic_set(&uv_nmi_cpus_in_nmi, k); | ||
355 | return n - k; | ||
356 | } | ||
357 | |||
358 | /* Wait until all slave cpus have entered UV NMI handler */ | ||
359 | static void uv_nmi_wait(int master) | ||
360 | { | ||
361 | /* indicate this cpu is in */ | ||
362 | atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_IN); | ||
363 | |||
364 | /* if not the first cpu in (the master), then we are a slave cpu */ | ||
365 | if (!master) | ||
366 | return; | ||
367 | |||
368 | do { | ||
369 | /* wait for all other cpus to gather here */ | ||
370 | if (!uv_nmi_wait_cpus(1)) | ||
371 | break; | ||
372 | |||
373 | /* if not all made it in, send IPI NMI to them */ | ||
374 | uv_nmi_nr_cpus_pr(KERN_ALERT | ||
375 | "UV: Sending NMI IPI to %d non-responding CPUs: %s\n"); | ||
376 | uv_nmi_nr_cpus_ping(); | ||
377 | |||
378 | /* if all cpus are in, then done */ | ||
379 | if (!uv_nmi_wait_cpus(0)) | ||
380 | break; | ||
381 | |||
382 | uv_nmi_nr_cpus_pr(KERN_ALERT | ||
383 | "UV: %d CPUs not in NMI loop: %s\n"); | ||
384 | } while (0); | ||
385 | |||
386 | pr_alert("UV: %d of %d CPUs in NMI\n", | ||
387 | atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus()); | ||
388 | } | ||
389 | |||
390 | static void uv_nmi_dump_cpu_ip_hdr(void) | ||
391 | { | ||
392 | printk(KERN_DEFAULT | ||
393 | "\nUV: %4s %6s %-32s %s (Note: PID 0 not listed)\n", | ||
394 | "CPU", "PID", "COMMAND", "IP"); | ||
395 | } | ||
396 | |||
397 | static void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs) | ||
398 | { | ||
399 | printk(KERN_DEFAULT "UV: %4d %6d %-32.32s ", | ||
400 | cpu, current->pid, current->comm); | ||
401 | |||
402 | printk_address(regs->ip, 1); | ||
403 | } | ||
404 | |||
405 | /* Dump this cpu's state */ | ||
406 | static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) | ||
407 | { | ||
408 | const char *dots = " ................................. "; | ||
409 | |||
410 | if (uv_nmi_action_is("ips")) { | ||
411 | if (cpu == 0) | ||
412 | uv_nmi_dump_cpu_ip_hdr(); | ||
413 | |||
414 | if (current->pid != 0) | ||
415 | uv_nmi_dump_cpu_ip(cpu, regs); | ||
416 | |||
417 | } else if (uv_nmi_action_is("dump")) { | ||
418 | printk(KERN_DEFAULT | ||
419 | "UV:%sNMI process trace for CPU %d\n", dots, cpu); | ||
420 | show_regs(regs); | ||
421 | } | ||
422 | atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); | ||
423 | } | ||
424 | |||
425 | /* Trigger a slave cpu to dump it's state */ | ||
426 | static void uv_nmi_trigger_dump(int cpu) | ||
427 | { | ||
428 | int retry = uv_nmi_trigger_delay; | ||
429 | |||
430 | if (atomic_read(&uv_cpu_nmi_per(cpu).state) != UV_NMI_STATE_IN) | ||
431 | return; | ||
432 | |||
433 | atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP); | ||
434 | do { | ||
435 | cpu_relax(); | ||
436 | udelay(10); | ||
437 | if (atomic_read(&uv_cpu_nmi_per(cpu).state) | ||
438 | != UV_NMI_STATE_DUMP) | ||
439 | return; | ||
440 | } while (--retry > 0); | ||
441 | |||
442 | pr_crit("UV: CPU %d stuck in process dump function\n", cpu); | ||
443 | atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP_DONE); | ||
444 | } | ||
445 | |||
446 | /* Wait until all cpus ready to exit */ | ||
447 | static void uv_nmi_sync_exit(int master) | ||
448 | { | ||
449 | atomic_dec(&uv_nmi_cpus_in_nmi); | ||
450 | if (master) { | ||
451 | while (atomic_read(&uv_nmi_cpus_in_nmi) > 0) | ||
452 | cpu_relax(); | ||
453 | atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR); | ||
454 | } else { | ||
455 | while (atomic_read(&uv_nmi_slave_continue)) | ||
456 | cpu_relax(); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | /* Walk through cpu list and dump state of each */ | ||
461 | static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) | ||
462 | { | ||
463 | if (master) { | ||
464 | int tcpu; | ||
465 | int ignored = 0; | ||
466 | int saved_console_loglevel = console_loglevel; | ||
467 | |||
468 | pr_alert("UV: tracing %s for %d CPUs from CPU %d\n", | ||
469 | uv_nmi_action_is("ips") ? "IPs" : "processes", | ||
470 | atomic_read(&uv_nmi_cpus_in_nmi), cpu); | ||
471 | |||
472 | console_loglevel = uv_nmi_loglevel; | ||
473 | atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); | ||
474 | for_each_online_cpu(tcpu) { | ||
475 | if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask)) | ||
476 | ignored++; | ||
477 | else if (tcpu == cpu) | ||
478 | uv_nmi_dump_state_cpu(tcpu, regs); | ||
479 | else | ||
480 | uv_nmi_trigger_dump(tcpu); | ||
481 | } | ||
482 | if (ignored) | ||
483 | printk(KERN_DEFAULT "UV: %d CPUs ignored NMI\n", | ||
484 | ignored); | ||
485 | |||
486 | console_loglevel = saved_console_loglevel; | ||
487 | pr_alert("UV: process trace complete\n"); | ||
488 | } else { | ||
489 | while (!atomic_read(&uv_nmi_slave_continue)) | ||
490 | cpu_relax(); | ||
491 | while (atomic_read(&uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) | ||
492 | cpu_relax(); | ||
493 | uv_nmi_dump_state_cpu(cpu, regs); | ||
494 | } | ||
495 | uv_nmi_sync_exit(master); | ||
496 | } | ||
497 | |||
498 | static void uv_nmi_touch_watchdogs(void) | ||
499 | { | ||
500 | touch_softlockup_watchdog_sync(); | ||
501 | clocksource_touch_watchdog(); | ||
502 | rcu_cpu_stall_reset(); | ||
503 | touch_nmi_watchdog(); | ||
504 | } | ||
505 | |||
506 | #if defined(CONFIG_KEXEC) | ||
507 | static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) | ||
508 | { | ||
509 | /* Call crash to dump system state */ | ||
510 | if (master) { | ||
511 | pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu); | ||
512 | crash_kexec(regs); | ||
513 | |||
514 | pr_emerg("UV: crash_kexec unexpectedly returned, "); | ||
515 | if (!kexec_crash_image) { | ||
516 | pr_cont("crash kernel not loaded\n"); | ||
517 | atomic_set(&uv_nmi_kexec_failed, 1); | ||
518 | uv_nmi_sync_exit(1); | ||
519 | return; | ||
520 | } | ||
521 | pr_cont("kexec busy, stalling cpus while waiting\n"); | ||
522 | } | ||
523 | |||
524 | /* If crash exec fails the slaves should return, otherwise stall */ | ||
525 | while (atomic_read(&uv_nmi_kexec_failed) == 0) | ||
526 | mdelay(10); | ||
527 | |||
528 | /* Crash kernel most likely not loaded, return in an orderly fashion */ | ||
529 | uv_nmi_sync_exit(0); | ||
530 | } | ||
531 | |||
532 | #else /* !CONFIG_KEXEC */ | ||
533 | static inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) | ||
534 | { | ||
535 | if (master) | ||
536 | pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n"); | ||
537 | } | ||
538 | #endif /* !CONFIG_KEXEC */ | ||
539 | |||
540 | #ifdef CONFIG_KGDB_KDB | ||
541 | /* Call KDB from NMI handler */ | ||
542 | static void uv_call_kdb(int cpu, struct pt_regs *regs, int master) | ||
543 | { | ||
544 | int ret; | ||
545 | |||
546 | if (master) { | ||
547 | /* call KGDB NMI handler as MASTER */ | ||
548 | ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, | ||
549 | &uv_nmi_slave_continue); | ||
550 | if (ret) { | ||
551 | pr_alert("KDB returned error, is kgdboc set?\n"); | ||
552 | atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); | ||
553 | } | ||
554 | } else { | ||
555 | /* wait for KGDB signal that it's ready for slaves to enter */ | ||
556 | int sig; | ||
557 | |||
558 | do { | ||
559 | cpu_relax(); | ||
560 | sig = atomic_read(&uv_nmi_slave_continue); | ||
561 | } while (!sig); | ||
562 | |||
563 | /* call KGDB as slave */ | ||
564 | if (sig == SLAVE_CONTINUE) | ||
565 | kgdb_nmicallback(cpu, regs); | ||
566 | } | ||
567 | uv_nmi_sync_exit(master); | ||
568 | } | ||
569 | |||
570 | #else /* !CONFIG_KGDB_KDB */ | ||
571 | static inline void uv_call_kdb(int cpu, struct pt_regs *regs, int master) | ||
572 | { | ||
573 | pr_err("UV: NMI error: KGDB/KDB is not enabled in this kernel\n"); | ||
574 | } | ||
575 | #endif /* !CONFIG_KGDB_KDB */ | ||
576 | |||
577 | /* | ||
578 | * UV NMI handler | ||
579 | */ | ||
580 | int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) | ||
581 | { | ||
582 | struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; | ||
583 | int cpu = smp_processor_id(); | ||
584 | int master = 0; | ||
585 | unsigned long flags; | ||
586 | |||
587 | local_irq_save(flags); | ||
588 | |||
589 | /* If not a UV System NMI, ignore */ | ||
590 | if (!atomic_read(&uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { | ||
591 | local_irq_restore(flags); | ||
592 | return NMI_DONE; | ||
593 | } | ||
594 | |||
595 | /* Indicate we are the first CPU into the NMI handler */ | ||
596 | master = (atomic_read(&uv_nmi_cpu) == cpu); | ||
597 | |||
598 | /* If NMI action is "kdump", then attempt to do it */ | ||
599 | if (uv_nmi_action_is("kdump")) | ||
600 | uv_nmi_kdump(cpu, master, regs); | ||
601 | |||
602 | /* Pause as all cpus enter the NMI handler */ | ||
603 | uv_nmi_wait(master); | ||
604 | |||
605 | /* Dump state of each cpu */ | ||
606 | if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) | ||
607 | uv_nmi_dump_state(cpu, regs, master); | ||
608 | |||
609 | /* Call KDB if enabled */ | ||
610 | else if (uv_nmi_action_is("kdb")) | ||
611 | uv_call_kdb(cpu, regs, master); | ||
612 | |||
613 | /* Clear per_cpu "in nmi" flag */ | ||
614 | atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); | ||
615 | |||
616 | /* Clear MMR NMI flag on each hub */ | ||
617 | uv_clear_nmi(cpu); | ||
618 | |||
619 | /* Clear global flags */ | ||
620 | if (master) { | ||
621 | if (cpumask_weight(uv_nmi_cpu_mask)) | ||
622 | uv_nmi_cleanup_mask(); | ||
623 | atomic_set(&uv_nmi_cpus_in_nmi, -1); | ||
624 | atomic_set(&uv_nmi_cpu, -1); | ||
625 | atomic_set(&uv_in_nmi, 0); | ||
626 | } | ||
627 | |||
628 | uv_nmi_touch_watchdogs(); | ||
629 | local_irq_restore(flags); | ||
630 | |||
631 | return NMI_HANDLED; | ||
632 | } | ||
633 | |||
634 | /* | ||
635 | * NMI handler for pulling in CPUs when perf events are grabbing our NMI | ||
636 | */ | ||
637 | int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) | ||
638 | { | ||
639 | int ret; | ||
640 | |||
641 | uv_cpu_nmi.queries++; | ||
642 | if (!atomic_read(&uv_cpu_nmi.pinging)) { | ||
643 | local64_inc(&uv_nmi_ping_misses); | ||
644 | return NMI_DONE; | ||
645 | } | ||
646 | |||
647 | uv_cpu_nmi.pings++; | ||
648 | local64_inc(&uv_nmi_ping_count); | ||
649 | ret = uv_handle_nmi(reason, regs); | ||
650 | atomic_set(&uv_cpu_nmi.pinging, 0); | ||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | void uv_register_nmi_notifier(void) | ||
655 | { | ||
656 | if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) | ||
657 | pr_warn("UV: NMI handler failed to register\n"); | ||
658 | |||
659 | if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) | ||
660 | pr_warn("UV: PING NMI handler failed to register\n"); | ||
661 | } | ||
662 | |||
663 | void uv_nmi_init(void) | ||
664 | { | ||
665 | unsigned int value; | ||
666 | |||
667 | /* | ||
668 | * Unmask NMI on all cpus | ||
669 | */ | ||
670 | value = apic_read(APIC_LVT1) | APIC_DM_NMI; | ||
671 | value &= ~APIC_LVT_MASKED; | ||
672 | apic_write(APIC_LVT1, value); | ||
673 | } | ||
674 | |||
675 | void uv_nmi_setup(void) | ||
676 | { | ||
677 | int size = sizeof(void *) * (1 << NODES_SHIFT); | ||
678 | int cpu, nid; | ||
679 | |||
680 | /* Setup hub nmi info */ | ||
681 | uv_nmi_setup_mmrs(); | ||
682 | uv_hub_nmi_list = kzalloc(size, GFP_KERNEL); | ||
683 | pr_info("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size); | ||
684 | BUG_ON(!uv_hub_nmi_list); | ||
685 | size = sizeof(struct uv_hub_nmi_s); | ||
686 | for_each_present_cpu(cpu) { | ||
687 | nid = cpu_to_node(cpu); | ||
688 | if (uv_hub_nmi_list[nid] == NULL) { | ||
689 | uv_hub_nmi_list[nid] = kzalloc_node(size, | ||
690 | GFP_KERNEL, nid); | ||
691 | BUG_ON(!uv_hub_nmi_list[nid]); | ||
692 | raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock)); | ||
693 | atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1); | ||
694 | } | ||
695 | uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid]; | ||
696 | } | ||
697 | BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL)); | ||
698 | } | ||
699 | |||
700 | |||