// SPDX-License-Identifier: GPL-2.0 /* * local apic based NMI watchdog for various CPUs. * * This file also handles reservation of performance counters for coordination * with other users. * * Note that these events normally don't tick when the CPU idles. This means * the frequency varies with CPU load. * * Original code for K7/P6 written by Keith Owens * */ #include <linux/percpu.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/bitops.h> #include <linux/smp.h> #include <asm/nmi.h> #include <linux/kprobes.h> #include <asm/apic.h> #include <asm/perf_event.h> /* * this number is calculated from Intel's MSR_P4_CRU_ESCR5 register and it's * offset from MSR_P4_BSU_ESCR0. * * It will be the max for all platforms (for now) */ #define NMI_MAX_COUNTER_BITS 66 /* * perfctr_nmi_owner tracks the ownership of the perfctr registers: * evtsel_nmi_owner tracks the ownership of the event selection * - different performance counters/ event selection may be reserved for * different subsystems this reservation system just tries to coordinate * things a little */ static DECLARE_BITMAP(perfctr_nmi_owner, NMI_MAX_COUNTER_BITS); static DECLARE_BITMAP(evntsel_nmi_owner, NMI_MAX_COUNTER_BITS); /* converts an msr to an appropriate reservation bit */ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr) { /* returns the bit offset of the performance counter register */ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_HYGON: case X86_VENDOR_AMD: if (msr >= MSR_F15H_PERF_CTR) return (msr - MSR_F15H_PERF_CTR) >> 1; return msr - MSR_K7_PERFCTR0; case X86_VENDOR_INTEL: if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) return msr - MSR_ARCH_PERFMON_PERFCTR0; switch (boot_cpu_data.x86) { case 6: return msr - MSR_P6_PERFCTR0; case 11: return msr - MSR_KNC_PERFCTR0; case 15: return msr - MSR_P4_BPU_PERFCTR0; } break; case X86_VENDOR_ZHAOXIN: case X86_VENDOR_CENTAUR: return msr - MSR_ARCH_PERFMON_PERFCTR0; } return 0; } /* * converts an msr to an appropriate reservation bit * returns the bit offset of the event selection register */ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr) { /* returns the bit offset of the event selection register */ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_HYGON: case X86_VENDOR_AMD: if (msr >= MSR_F15H_PERF_CTL) return (msr - MSR_F15H_PERF_CTL) >> 1; return msr - MSR_K7_EVNTSEL0; case X86_VENDOR_INTEL: if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) return msr - MSR_ARCH_PERFMON_EVENTSEL0; switch (boot_cpu_data.x86) { case 6: return msr - MSR_P6_EVNTSEL0; case 11: return msr - MSR_KNC_EVNTSEL0; case 15: return msr - MSR_P4_BSU_ESCR0; } break; case X86_VENDOR_ZHAOXIN: case X86_VENDOR_CENTAUR: return msr - MSR_ARCH_PERFMON_EVENTSEL0; } return 0; } int reserve_perfctr_nmi(unsigned int msr) { unsigned int counter; counter = nmi_perfctr_msr_to_bit(msr); /* register not managed by the allocator? */ if (counter > NMI_MAX_COUNTER_BITS) return 1; if (!test_and_set_bit(counter, perfctr_nmi_owner)) return 1; return 0; } EXPORT_SYMBOL(reserve_perfctr_nmi); void release_perfctr_nmi(unsigned int msr) { unsigned int counter; counter = nmi_perfctr_msr_to_bit(msr); /* register not managed by the allocator? */ if (counter > NMI_MAX_COUNTER_BITS) return; clear_bit(counter, perfctr_nmi_owner); } EXPORT_SYMBOL(release_perfctr_nmi); int reserve_evntsel_nmi(unsigned int msr) { unsigned int counter; counter = nmi_evntsel_msr_to_bit(msr); /* register not managed by the allocator? */ if (counter > NMI_MAX_COUNTER_BITS) return 1; if (!test_and_set_bit(counter, evntsel_nmi_owner)) return 1; return 0; } EXPORT_SYMBOL(reserve_evntsel_nmi); void release_evntsel_nmi(unsigned int msr) { unsigned int counter; counter = nmi_evntsel_msr_to_bit(msr); /* register not managed by the allocator? */ if (counter > NMI_MAX_COUNTER_BITS) return; clear_bit(counter, evntsel_nmi_owner); } EXPORT_SYMBOL