18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * @file nmi_int.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * @remark Copyright 2002-2009 OProfile authors 58c2ecf20Sopenharmony_ci * @remark Read the file COPYING 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * @author John Levon <levon@movementarian.org> 88c2ecf20Sopenharmony_ci * @author Robert Richter <robert.richter@amd.com> 98c2ecf20Sopenharmony_ci * @author Barry Kasindorf <barry.kasindorf@amd.com> 108c2ecf20Sopenharmony_ci * @author Jason Yeh <jason.yeh@amd.com> 118c2ecf20Sopenharmony_ci * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/notifier.h> 168c2ecf20Sopenharmony_ci#include <linux/smp.h> 178c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 188c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 218c2ecf20Sopenharmony_ci#include <linux/kdebug.h> 228c2ecf20Sopenharmony_ci#include <linux/cpu.h> 238c2ecf20Sopenharmony_ci#include <asm/nmi.h> 248c2ecf20Sopenharmony_ci#include <asm/msr.h> 258c2ecf20Sopenharmony_ci#include <asm/apic.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "op_counter.h" 288c2ecf20Sopenharmony_ci#include "op_x86_model.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct op_x86_model_spec *model; 318c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct op_msrs, cpu_msrs); 328c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, saved_lvtpc); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* must be protected with get_online_cpus()/put_online_cpus(): */ 358c2ecf20Sopenharmony_cistatic int nmi_enabled; 368c2ecf20Sopenharmony_cistatic int ctr_running; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct op_counter_config counter_config[OP_MAX_COUNTER]; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* common functions */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciu64 op_x86_get_ctrl(struct op_x86_model_spec const *model, 438c2ecf20Sopenharmony_ci struct op_counter_config *counter_config) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci u64 val = 0; 468c2ecf20Sopenharmony_ci u16 event = (u16)counter_config->event; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci val |= ARCH_PERFMON_EVENTSEL_INT; 498c2ecf20Sopenharmony_ci val |= counter_config->user ? ARCH_PERFMON_EVENTSEL_USR : 0; 508c2ecf20Sopenharmony_ci val |= counter_config->kernel ? ARCH_PERFMON_EVENTSEL_OS : 0; 518c2ecf20Sopenharmony_ci val |= (counter_config->unit_mask & 0xFF) << 8; 528c2ecf20Sopenharmony_ci counter_config->extra &= (ARCH_PERFMON_EVENTSEL_INV | 538c2ecf20Sopenharmony_ci ARCH_PERFMON_EVENTSEL_EDGE | 548c2ecf20Sopenharmony_ci ARCH_PERFMON_EVENTSEL_CMASK); 558c2ecf20Sopenharmony_ci val |= counter_config->extra; 568c2ecf20Sopenharmony_ci event &= model->event_mask ? model->event_mask : 0xFF; 578c2ecf20Sopenharmony_ci val |= event & 0xFF; 588c2ecf20Sopenharmony_ci val |= (u64)(event & 0x0F00) << 24; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return val; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int profile_exceptions_notify(unsigned int val, struct pt_regs *regs) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (ctr_running) 678c2ecf20Sopenharmony_ci model->check_ctrs(regs, this_cpu_ptr(&cpu_msrs)); 688c2ecf20Sopenharmony_ci else if (!nmi_enabled) 698c2ecf20Sopenharmony_ci return NMI_DONE; 708c2ecf20Sopenharmony_ci else 718c2ecf20Sopenharmony_ci model->stop(this_cpu_ptr(&cpu_msrs)); 728c2ecf20Sopenharmony_ci return NMI_HANDLED; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void nmi_cpu_save_registers(struct op_msrs *msrs) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct op_msr *counters = msrs->counters; 788c2ecf20Sopenharmony_ci struct op_msr *controls = msrs->controls; 798c2ecf20Sopenharmony_ci unsigned int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < model->num_counters; ++i) { 828c2ecf20Sopenharmony_ci if (counters[i].addr) 838c2ecf20Sopenharmony_ci rdmsrl(counters[i].addr, counters[i].saved); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci for (i = 0; i < model->num_controls; ++i) { 878c2ecf20Sopenharmony_ci if (controls[i].addr) 888c2ecf20Sopenharmony_ci rdmsrl(controls[i].addr, controls[i].saved); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void nmi_cpu_start(void *dummy) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct op_msrs const *msrs = this_cpu_ptr(&cpu_msrs); 958c2ecf20Sopenharmony_ci if (!msrs->controls) 968c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci model->start(msrs); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int nmi_start(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci get_online_cpus(); 1048c2ecf20Sopenharmony_ci ctr_running = 1; 1058c2ecf20Sopenharmony_ci /* make ctr_running visible to the nmi handler: */ 1068c2ecf20Sopenharmony_ci smp_mb(); 1078c2ecf20Sopenharmony_ci on_each_cpu(nmi_cpu_start, NULL, 1); 1088c2ecf20Sopenharmony_ci put_online_cpus(); 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void nmi_cpu_stop(void *dummy) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct op_msrs const *msrs = this_cpu_ptr(&cpu_msrs); 1158c2ecf20Sopenharmony_ci if (!msrs->controls) 1168c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci model->stop(msrs); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void nmi_stop(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci get_online_cpus(); 1248c2ecf20Sopenharmony_ci on_each_cpu(nmi_cpu_stop, NULL, 1); 1258c2ecf20Sopenharmony_ci ctr_running = 0; 1268c2ecf20Sopenharmony_ci put_online_cpus(); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(int, switch_index); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic inline int has_mux(void) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci return !!model->switch_ctrl; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciinline int op_x86_phys_to_virt(int phys) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return __this_cpu_read(switch_index) + phys; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciinline int op_x86_virt_to_phys(int virt) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return virt % model->num_counters; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void nmi_shutdown_mux(void) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int i; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!has_mux()) 1538c2ecf20Sopenharmony_ci return; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 1568c2ecf20Sopenharmony_ci kfree(per_cpu(cpu_msrs, i).multiplex); 1578c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).multiplex = NULL; 1588c2ecf20Sopenharmony_ci per_cpu(switch_index, i) = 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int nmi_setup_mux(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci size_t multiplex_size = 1658c2ecf20Sopenharmony_ci sizeof(struct op_msr) * model->num_virt_counters; 1668c2ecf20Sopenharmony_ci int i; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!has_mux()) 1698c2ecf20Sopenharmony_ci return 1; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 1728c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).multiplex = 1738c2ecf20Sopenharmony_ci kzalloc(multiplex_size, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!per_cpu(cpu_msrs, i).multiplex) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 1; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int i; 1848c2ecf20Sopenharmony_ci struct op_msr *multiplex = msrs->multiplex; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!has_mux()) 1878c2ecf20Sopenharmony_ci return; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for (i = 0; i < model->num_virt_counters; ++i) { 1908c2ecf20Sopenharmony_ci if (counter_config[i].enabled) { 1918c2ecf20Sopenharmony_ci multiplex[i].saved = -(u64)counter_config[i].count; 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci multiplex[i].saved = 0; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci per_cpu(switch_index, cpu) = 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void nmi_cpu_save_mpx_registers(struct op_msrs *msrs) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct op_msr *counters = msrs->counters; 2038c2ecf20Sopenharmony_ci struct op_msr *multiplex = msrs->multiplex; 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < model->num_counters; ++i) { 2078c2ecf20Sopenharmony_ci int virt = op_x86_phys_to_virt(i); 2088c2ecf20Sopenharmony_ci if (counters[i].addr) 2098c2ecf20Sopenharmony_ci rdmsrl(counters[i].addr, multiplex[virt].saved); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void nmi_cpu_restore_mpx_registers(struct op_msrs *msrs) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct op_msr *counters = msrs->counters; 2168c2ecf20Sopenharmony_ci struct op_msr *multiplex = msrs->multiplex; 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < model->num_counters; ++i) { 2208c2ecf20Sopenharmony_ci int virt = op_x86_phys_to_virt(i); 2218c2ecf20Sopenharmony_ci if (counters[i].addr) 2228c2ecf20Sopenharmony_ci wrmsrl(counters[i].addr, multiplex[virt].saved); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void nmi_cpu_switch(void *dummy) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 2298c2ecf20Sopenharmony_ci int si = per_cpu(switch_index, cpu); 2308c2ecf20Sopenharmony_ci struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci nmi_cpu_stop(NULL); 2338c2ecf20Sopenharmony_ci nmi_cpu_save_mpx_registers(msrs); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* move to next set */ 2368c2ecf20Sopenharmony_ci si += model->num_counters; 2378c2ecf20Sopenharmony_ci if ((si >= model->num_virt_counters) || (counter_config[si].count == 0)) 2388c2ecf20Sopenharmony_ci per_cpu(switch_index, cpu) = 0; 2398c2ecf20Sopenharmony_ci else 2408c2ecf20Sopenharmony_ci per_cpu(switch_index, cpu) = si; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci model->switch_ctrl(model, msrs); 2438c2ecf20Sopenharmony_ci nmi_cpu_restore_mpx_registers(msrs); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci nmi_cpu_start(NULL); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * Quick check to see if multiplexing is necessary. 2518c2ecf20Sopenharmony_ci * The check should be sufficient since counters are used 2528c2ecf20Sopenharmony_ci * in ordre. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic int nmi_multiplex_on(void) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci return counter_config[model->num_counters].count ? 0 : -EINVAL; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int nmi_switch_event(void) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (!has_mux()) 2628c2ecf20Sopenharmony_ci return -ENOSYS; /* not implemented */ 2638c2ecf20Sopenharmony_ci if (nmi_multiplex_on() < 0) 2648c2ecf20Sopenharmony_ci return -EINVAL; /* not necessary */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci get_online_cpus(); 2678c2ecf20Sopenharmony_ci if (ctr_running) 2688c2ecf20Sopenharmony_ci on_each_cpu(nmi_cpu_switch, NULL, 1); 2698c2ecf20Sopenharmony_ci put_online_cpus(); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic inline void mux_init(struct oprofile_operations *ops) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (has_mux()) 2778c2ecf20Sopenharmony_ci ops->switch_events = nmi_switch_event; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void mux_clone(int cpu) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci if (!has_mux()) 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci memcpy(per_cpu(cpu_msrs, cpu).multiplex, 2868c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, 0).multiplex, 2878c2ecf20Sopenharmony_ci sizeof(struct op_msr) * model->num_virt_counters); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#else 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciinline int op_x86_phys_to_virt(int phys) { return phys; } 2938c2ecf20Sopenharmony_ciinline int op_x86_virt_to_phys(int virt) { return virt; } 2948c2ecf20Sopenharmony_cistatic inline void nmi_shutdown_mux(void) { } 2958c2ecf20Sopenharmony_cistatic inline int nmi_setup_mux(void) { return 1; } 2968c2ecf20Sopenharmony_cistatic inline void 2978c2ecf20Sopenharmony_cinmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) { } 2988c2ecf20Sopenharmony_cistatic inline void mux_init(struct oprofile_operations *ops) { } 2998c2ecf20Sopenharmony_cistatic void mux_clone(int cpu) { } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#endif 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void free_msrs(void) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 3078c2ecf20Sopenharmony_ci kfree(per_cpu(cpu_msrs, i).counters); 3088c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).counters = NULL; 3098c2ecf20Sopenharmony_ci kfree(per_cpu(cpu_msrs, i).controls); 3108c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).controls = NULL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci nmi_shutdown_mux(); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int allocate_msrs(void) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci size_t controls_size = sizeof(struct op_msr) * model->num_controls; 3188c2ecf20Sopenharmony_ci size_t counters_size = sizeof(struct op_msr) * model->num_counters; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 3228c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).counters = kzalloc(counters_size, 3238c2ecf20Sopenharmony_ci GFP_KERNEL); 3248c2ecf20Sopenharmony_ci if (!per_cpu(cpu_msrs, i).counters) 3258c2ecf20Sopenharmony_ci goto fail; 3268c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, i).controls = kzalloc(controls_size, 3278c2ecf20Sopenharmony_ci GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!per_cpu(cpu_msrs, i).controls) 3298c2ecf20Sopenharmony_ci goto fail; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!nmi_setup_mux()) 3338c2ecf20Sopenharmony_ci goto fail; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 1; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cifail: 3388c2ecf20Sopenharmony_ci free_msrs(); 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void nmi_cpu_setup(void) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 3458c2ecf20Sopenharmony_ci struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci nmi_cpu_save_registers(msrs); 3488c2ecf20Sopenharmony_ci raw_spin_lock(&oprofilefs_lock); 3498c2ecf20Sopenharmony_ci model->setup_ctrs(model, msrs); 3508c2ecf20Sopenharmony_ci nmi_cpu_setup_mux(cpu, msrs); 3518c2ecf20Sopenharmony_ci raw_spin_unlock(&oprofilefs_lock); 3528c2ecf20Sopenharmony_ci per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC); 3538c2ecf20Sopenharmony_ci apic_write(APIC_LVTPC, APIC_DM_NMI); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void nmi_cpu_restore_registers(struct op_msrs *msrs) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct op_msr *counters = msrs->counters; 3598c2ecf20Sopenharmony_ci struct op_msr *controls = msrs->controls; 3608c2ecf20Sopenharmony_ci unsigned int i; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci for (i = 0; i < model->num_controls; ++i) { 3638c2ecf20Sopenharmony_ci if (controls[i].addr) 3648c2ecf20Sopenharmony_ci wrmsrl(controls[i].addr, controls[i].saved); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < model->num_counters; ++i) { 3688c2ecf20Sopenharmony_ci if (counters[i].addr) 3698c2ecf20Sopenharmony_ci wrmsrl(counters[i].addr, counters[i].saved); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void nmi_cpu_shutdown(void) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci unsigned int v; 3768c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 3778c2ecf20Sopenharmony_ci struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* restoring APIC_LVTPC can trigger an apic error because the delivery 3808c2ecf20Sopenharmony_ci * mode and vector nr combination can be illegal. That's by design: on 3818c2ecf20Sopenharmony_ci * power on apic lvt contain a zero vector nr which are legal only for 3828c2ecf20Sopenharmony_ci * NMI delivery mode. So inhibit apic err before restoring lvtpc 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci v = apic_read(APIC_LVTERR); 3858c2ecf20Sopenharmony_ci apic_write(APIC_LVTERR, v | APIC_LVT_MASKED); 3868c2ecf20Sopenharmony_ci apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); 3878c2ecf20Sopenharmony_ci apic_write(APIC_LVTERR, v); 3888c2ecf20Sopenharmony_ci nmi_cpu_restore_registers(msrs); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int nmi_cpu_online(unsigned int cpu) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci local_irq_disable(); 3948c2ecf20Sopenharmony_ci if (nmi_enabled) 3958c2ecf20Sopenharmony_ci nmi_cpu_setup(); 3968c2ecf20Sopenharmony_ci if (ctr_running) 3978c2ecf20Sopenharmony_ci nmi_cpu_start(NULL); 3988c2ecf20Sopenharmony_ci local_irq_enable(); 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int nmi_cpu_down_prep(unsigned int cpu) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci local_irq_disable(); 4058c2ecf20Sopenharmony_ci if (ctr_running) 4068c2ecf20Sopenharmony_ci nmi_cpu_stop(NULL); 4078c2ecf20Sopenharmony_ci if (nmi_enabled) 4088c2ecf20Sopenharmony_ci nmi_cpu_shutdown(); 4098c2ecf20Sopenharmony_ci local_irq_enable(); 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int nmi_create_files(struct dentry *root) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci unsigned int i; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = 0; i < model->num_virt_counters; ++i) { 4188c2ecf20Sopenharmony_ci struct dentry *dir; 4198c2ecf20Sopenharmony_ci char buf[4]; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* quick little hack to _not_ expose a counter if it is not 4228c2ecf20Sopenharmony_ci * available for use. This should protect userspace app. 4238c2ecf20Sopenharmony_ci * NOTE: assumes 1:1 mapping here (that counters are organized 4248c2ecf20Sopenharmony_ci * sequentially in their struct assignment). 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci if (!avail_to_resrv_perfctr_nmi_bit(op_x86_virt_to_phys(i))) 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%d", i); 4308c2ecf20Sopenharmony_ci dir = oprofilefs_mkdir(root, buf); 4318c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "enabled", &counter_config[i].enabled); 4328c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "event", &counter_config[i].event); 4338c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "count", &counter_config[i].count); 4348c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "unit_mask", &counter_config[i].unit_mask); 4358c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "kernel", &counter_config[i].kernel); 4368c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "user", &counter_config[i].user); 4378c2ecf20Sopenharmony_ci oprofilefs_create_ulong(dir, "extra", &counter_config[i].extra); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic enum cpuhp_state cpuhp_nmi_online; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int nmi_setup(void) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci int err = 0; 4488c2ecf20Sopenharmony_ci int cpu; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (!allocate_msrs()) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* We need to serialize save and setup for HT because the subset 4548c2ecf20Sopenharmony_ci * of msrs are distinct for save and setup operations 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Assume saved/restored counters are the same on all CPUs */ 4588c2ecf20Sopenharmony_ci err = model->fill_in_addresses(&per_cpu(cpu_msrs, 0)); 4598c2ecf20Sopenharmony_ci if (err) 4608c2ecf20Sopenharmony_ci goto fail; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 4638c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_SMP) || !cpu) 4648c2ecf20Sopenharmony_ci continue; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci memcpy(per_cpu(cpu_msrs, cpu).counters, 4678c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, 0).counters, 4688c2ecf20Sopenharmony_ci sizeof(struct op_msr) * model->num_counters); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci memcpy(per_cpu(cpu_msrs, cpu).controls, 4718c2ecf20Sopenharmony_ci per_cpu(cpu_msrs, 0).controls, 4728c2ecf20Sopenharmony_ci sizeof(struct op_msr) * model->num_controls); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mux_clone(cpu); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci nmi_enabled = 0; 4788c2ecf20Sopenharmony_ci ctr_running = 0; 4798c2ecf20Sopenharmony_ci /* make variables visible to the nmi handler: */ 4808c2ecf20Sopenharmony_ci smp_mb(); 4818c2ecf20Sopenharmony_ci err = register_nmi_handler(NMI_LOCAL, profile_exceptions_notify, 4828c2ecf20Sopenharmony_ci 0, "oprofile"); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci goto fail; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci nmi_enabled = 1; 4878c2ecf20Sopenharmony_ci /* make nmi_enabled visible to the nmi handler: */ 4888c2ecf20Sopenharmony_ci smp_mb(); 4898c2ecf20Sopenharmony_ci err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/oprofile:online", 4908c2ecf20Sopenharmony_ci nmi_cpu_online, nmi_cpu_down_prep); 4918c2ecf20Sopenharmony_ci if (err < 0) 4928c2ecf20Sopenharmony_ci goto fail_nmi; 4938c2ecf20Sopenharmony_ci cpuhp_nmi_online = err; 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_cifail_nmi: 4968c2ecf20Sopenharmony_ci unregister_nmi_handler(NMI_LOCAL, "oprofile"); 4978c2ecf20Sopenharmony_cifail: 4988c2ecf20Sopenharmony_ci free_msrs(); 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void nmi_shutdown(void) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct op_msrs *msrs; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci cpuhp_remove_state(cpuhp_nmi_online); 5078c2ecf20Sopenharmony_ci nmi_enabled = 0; 5088c2ecf20Sopenharmony_ci ctr_running = 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* make variables visible to the nmi handler: */ 5118c2ecf20Sopenharmony_ci smp_mb(); 5128c2ecf20Sopenharmony_ci unregister_nmi_handler(NMI_LOCAL, "oprofile"); 5138c2ecf20Sopenharmony_ci msrs = &get_cpu_var(cpu_msrs); 5148c2ecf20Sopenharmony_ci model->shutdown(msrs); 5158c2ecf20Sopenharmony_ci free_msrs(); 5168c2ecf20Sopenharmony_ci put_cpu_var(cpu_msrs); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int nmi_suspend(void) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci /* Only one CPU left, just stop that one */ 5248c2ecf20Sopenharmony_ci if (nmi_enabled == 1) 5258c2ecf20Sopenharmony_ci nmi_cpu_stop(NULL); 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void nmi_resume(void) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci if (nmi_enabled == 1) 5328c2ecf20Sopenharmony_ci nmi_cpu_start(NULL); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic struct syscore_ops oprofile_syscore_ops = { 5368c2ecf20Sopenharmony_ci .resume = nmi_resume, 5378c2ecf20Sopenharmony_ci .suspend = nmi_suspend, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic void __init init_suspend_resume(void) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci register_syscore_ops(&oprofile_syscore_ops); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void exit_suspend_resume(void) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci unregister_syscore_ops(&oprofile_syscore_ops); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci#else 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic inline void init_suspend_resume(void) { } 5538c2ecf20Sopenharmony_cistatic inline void exit_suspend_resume(void) { } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int __init p4_init(char **cpu_type) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci __u8 cpu_model = boot_cpu_data.x86_model; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (cpu_model > 6 || cpu_model == 5) 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci#ifndef CONFIG_SMP 5658c2ecf20Sopenharmony_ci *cpu_type = "i386/p4"; 5668c2ecf20Sopenharmony_ci model = &op_p4_spec; 5678c2ecf20Sopenharmony_ci return 1; 5688c2ecf20Sopenharmony_ci#else 5698c2ecf20Sopenharmony_ci switch (smp_num_siblings) { 5708c2ecf20Sopenharmony_ci case 1: 5718c2ecf20Sopenharmony_ci *cpu_type = "i386/p4"; 5728c2ecf20Sopenharmony_ci model = &op_p4_spec; 5738c2ecf20Sopenharmony_ci return 1; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci case 2: 5768c2ecf20Sopenharmony_ci *cpu_type = "i386/p4-ht"; 5778c2ecf20Sopenharmony_ci model = &op_p4_ht2_spec; 5788c2ecf20Sopenharmony_ci return 1; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci#endif 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: P4 HyperThreading detected with > 2 threads\n"); 5838c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: Reverting to timer mode.\n"); 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cienum __force_cpu_type { 5888c2ecf20Sopenharmony_ci reserved = 0, /* do not force */ 5898c2ecf20Sopenharmony_ci timer, 5908c2ecf20Sopenharmony_ci arch_perfmon, 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int force_cpu_type; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int set_cpu_type(const char *str, const struct kernel_param *kp) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci if (!strcmp(str, "timer")) { 5988c2ecf20Sopenharmony_ci force_cpu_type = timer; 5998c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: forcing NMI timer mode\n"); 6008c2ecf20Sopenharmony_ci } else if (!strcmp(str, "arch_perfmon")) { 6018c2ecf20Sopenharmony_ci force_cpu_type = arch_perfmon; 6028c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: forcing architectural perfmon\n"); 6038c2ecf20Sopenharmony_ci } else { 6048c2ecf20Sopenharmony_ci force_cpu_type = 0; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_cimodule_param_call(cpu_type, set_cpu_type, NULL, NULL, 0); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int __init ppro_init(char **cpu_type) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci __u8 cpu_model = boot_cpu_data.x86_model; 6148c2ecf20Sopenharmony_ci struct op_x86_model_spec *spec = &op_ppro_spec; /* default */ 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (force_cpu_type == arch_perfmon && boot_cpu_has(X86_FEATURE_ARCH_PERFMON)) 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * Documentation on identifying Intel processors by CPU family 6218c2ecf20Sopenharmony_ci * and model can be found in the Intel Software Developer's 6228c2ecf20Sopenharmony_ci * Manuals (SDM): 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * http://www.intel.com/products/processor/manuals/ 6258c2ecf20Sopenharmony_ci * 6268c2ecf20Sopenharmony_ci * As of May 2010 the documentation for this was in the: 6278c2ecf20Sopenharmony_ci * "Intel 64 and IA-32 Architectures Software Developer's 6288c2ecf20Sopenharmony_ci * Manual Volume 3B: System Programming Guide", "Table B-1 6298c2ecf20Sopenharmony_ci * CPUID Signature Values of DisplayFamily_DisplayModel". 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci switch (cpu_model) { 6328c2ecf20Sopenharmony_ci case 0 ... 2: 6338c2ecf20Sopenharmony_ci *cpu_type = "i386/ppro"; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case 3 ... 5: 6368c2ecf20Sopenharmony_ci *cpu_type = "i386/pii"; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci case 6 ... 8: 6398c2ecf20Sopenharmony_ci case 10 ... 11: 6408c2ecf20Sopenharmony_ci *cpu_type = "i386/piii"; 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci case 9: 6438c2ecf20Sopenharmony_ci case 13: 6448c2ecf20Sopenharmony_ci *cpu_type = "i386/p6_mobile"; 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci case 14: 6478c2ecf20Sopenharmony_ci *cpu_type = "i386/core"; 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci case 0x0f: 6508c2ecf20Sopenharmony_ci case 0x16: 6518c2ecf20Sopenharmony_ci case 0x17: 6528c2ecf20Sopenharmony_ci case 0x1d: 6538c2ecf20Sopenharmony_ci *cpu_type = "i386/core_2"; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci case 0x1a: 6568c2ecf20Sopenharmony_ci case 0x1e: 6578c2ecf20Sopenharmony_ci case 0x2e: 6588c2ecf20Sopenharmony_ci spec = &op_arch_perfmon_spec; 6598c2ecf20Sopenharmony_ci *cpu_type = "i386/core_i7"; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci case 0x1c: 6628c2ecf20Sopenharmony_ci *cpu_type = "i386/atom"; 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci default: 6658c2ecf20Sopenharmony_ci /* Unknown */ 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci model = spec; 6708c2ecf20Sopenharmony_ci return 1; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ciint __init op_nmi_init(struct oprofile_operations *ops) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci __u8 vendor = boot_cpu_data.x86_vendor; 6768c2ecf20Sopenharmony_ci __u8 family = boot_cpu_data.x86; 6778c2ecf20Sopenharmony_ci char *cpu_type = NULL; 6788c2ecf20Sopenharmony_ci int ret = 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_APIC)) 6818c2ecf20Sopenharmony_ci return -ENODEV; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (force_cpu_type == timer) 6848c2ecf20Sopenharmony_ci return -ENODEV; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci switch (vendor) { 6878c2ecf20Sopenharmony_ci case X86_VENDOR_AMD: 6888c2ecf20Sopenharmony_ci /* Needs to be at least an Athlon (or hammer in 32bit mode) */ 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci switch (family) { 6918c2ecf20Sopenharmony_ci case 6: 6928c2ecf20Sopenharmony_ci cpu_type = "i386/athlon"; 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci case 0xf: 6958c2ecf20Sopenharmony_ci /* 6968c2ecf20Sopenharmony_ci * Actually it could be i386/hammer too, but 6978c2ecf20Sopenharmony_ci * give user space an consistent name. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci cpu_type = "x86-64/hammer"; 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci case 0x10: 7028c2ecf20Sopenharmony_ci cpu_type = "x86-64/family10"; 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci case 0x11: 7058c2ecf20Sopenharmony_ci cpu_type = "x86-64/family11h"; 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci case 0x12: 7088c2ecf20Sopenharmony_ci cpu_type = "x86-64/family12h"; 7098c2ecf20Sopenharmony_ci break; 7108c2ecf20Sopenharmony_ci case 0x14: 7118c2ecf20Sopenharmony_ci cpu_type = "x86-64/family14h"; 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case 0x15: 7148c2ecf20Sopenharmony_ci cpu_type = "x86-64/family15h"; 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci default: 7178c2ecf20Sopenharmony_ci return -ENODEV; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci model = &op_amd_spec; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci case X86_VENDOR_INTEL: 7238c2ecf20Sopenharmony_ci switch (family) { 7248c2ecf20Sopenharmony_ci /* Pentium IV */ 7258c2ecf20Sopenharmony_ci case 0xf: 7268c2ecf20Sopenharmony_ci p4_init(&cpu_type); 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* A P6-class processor */ 7308c2ecf20Sopenharmony_ci case 6: 7318c2ecf20Sopenharmony_ci ppro_init(&cpu_type); 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci default: 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (cpu_type) 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_ARCH_PERFMON)) 7428c2ecf20Sopenharmony_ci return -ENODEV; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* use arch perfmon as fallback */ 7458c2ecf20Sopenharmony_ci cpu_type = "i386/arch_perfmon"; 7468c2ecf20Sopenharmony_ci model = &op_arch_perfmon_spec; 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci default: 7508c2ecf20Sopenharmony_ci return -ENODEV; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* default values, can be overwritten by model */ 7548c2ecf20Sopenharmony_ci ops->create_files = nmi_create_files; 7558c2ecf20Sopenharmony_ci ops->setup = nmi_setup; 7568c2ecf20Sopenharmony_ci ops->shutdown = nmi_shutdown; 7578c2ecf20Sopenharmony_ci ops->start = nmi_start; 7588c2ecf20Sopenharmony_ci ops->stop = nmi_stop; 7598c2ecf20Sopenharmony_ci ops->cpu_type = cpu_type; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (model->init) 7628c2ecf20Sopenharmony_ci ret = model->init(ops); 7638c2ecf20Sopenharmony_ci if (ret) 7648c2ecf20Sopenharmony_ci return ret; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (!model->num_virt_counters) 7678c2ecf20Sopenharmony_ci model->num_virt_counters = model->num_counters; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci mux_init(ops); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci init_suspend_resume(); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci printk(KERN_INFO "oprofile: using NMI interrupt.\n"); 7748c2ecf20Sopenharmony_ci return 0; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_civoid op_nmi_exit(void) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci exit_suspend_resume(); 7808c2ecf20Sopenharmony_ci} 781