2008-02-20 01:51:27 +03:00
/*
2008-07-22 23:09:08 +04:00
* @ file op_model_amd . c
2007-12-18 20:05:58 +03:00
* athlon / K7 / K8 / Family 10 h model - specific MSR operations
2005-04-17 02:20:36 +04:00
*
2008-07-22 23:08:48 +04:00
* @ remark Copyright 2002 - 2008 OProfile authors
2005-04-17 02:20:36 +04:00
* @ remark Read the file COPYING
*
* @ author John Levon
* @ author Philippe Elie
* @ author Graydon Hoare
2008-07-22 23:08:48 +04:00
* @ author Robert Richter < robert . richter @ amd . com >
2008-07-22 23:08:55 +04:00
* @ author Barry Kasindorf
2008-07-22 23:08:48 +04:00
*/
2005-04-17 02:20:36 +04:00
# include <linux/oprofile.h>
2008-07-22 23:08:55 +04:00
# include <linux/device.h>
# include <linux/pci.h>
2005-04-17 02:20:36 +04:00
# include <asm/ptrace.h>
# include <asm/msr.h>
2006-06-26 15:57:01 +04:00
# include <asm/nmi.h>
2008-02-20 01:51:27 +03:00
2005-04-17 02:20:36 +04:00
# include "op_x86_model.h"
# include "op_counter.h"
2008-09-24 13:08:52 +04:00
# define NUM_COUNTERS 4
# define NUM_CONTROLS 4
2005-04-17 02:20:36 +04:00
2008-02-20 01:51:27 +03:00
# define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
# define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
# define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
2005-04-17 02:20:36 +04:00
# define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
2008-02-20 01:51:27 +03:00
# define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
# define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
# define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
2005-04-17 02:20:36 +04:00
# define CTRL_SET_ACTIVE(n) (n |= (1<<22))
# define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
2007-12-18 20:05:58 +03:00
# define CTRL_CLEAR_LO(x) (x &= (1<<21))
# define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
2005-04-17 02:20:36 +04:00
# define CTRL_SET_ENABLE(val) (val |= 1<<20)
2008-02-20 01:51:27 +03:00
# define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
# define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
2005-04-17 02:20:36 +04:00
# define CTRL_SET_UM(val, m) (val |= (m << 8))
2007-12-18 20:05:58 +03:00
# define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
# define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
# define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
# define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
2005-04-17 02:20:36 +04:00
2008-07-22 23:09:06 +04:00
static unsigned long reset_value [ NUM_COUNTERS ] ;
# ifdef CONFIG_OPROFILE_IBS
2008-07-22 23:09:03 +04:00
/* IbsFetchCtl bits/masks */
# define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */
# define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */
# define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */
2008-07-22 23:08:55 +04:00
2008-07-22 23:09:03 +04:00
/*IbsOpCtl bits */
# define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */
# define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */
2008-07-22 23:08:55 +04:00
/* Codes used in cpu_buffer.c */
2008-07-22 23:09:03 +04:00
/* This produces duplicate code, need to be fixed */
2008-07-22 23:08:55 +04:00
# define IBS_FETCH_BEGIN 3
# define IBS_OP_BEGIN 4
2008-07-22 23:08:58 +04:00
/* The function interface needs to be fixed, something like add
data . Should then be added to linux / oprofile . h . */
2008-07-18 19:36:20 +04:00
extern void
oprofile_add_ibs_sample ( struct pt_regs * const regs ,
2008-09-05 19:12:36 +04:00
unsigned int * const ibs_sample , int ibs_code ) ;
2008-07-22 23:08:58 +04:00
2008-07-22 23:08:55 +04:00
struct ibs_fetch_sample {
/* MSRC001_1031 IBS Fetch Linear Address Register */
unsigned int ibs_fetch_lin_addr_low ;
unsigned int ibs_fetch_lin_addr_high ;
/* MSRC001_1030 IBS Fetch Control Register */
unsigned int ibs_fetch_ctl_low ;
unsigned int ibs_fetch_ctl_high ;
/* MSRC001_1032 IBS Fetch Physical Address Register */
unsigned int ibs_fetch_phys_addr_low ;
unsigned int ibs_fetch_phys_addr_high ;
} ;
struct ibs_op_sample {
/* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */
unsigned int ibs_op_rip_low ;
unsigned int ibs_op_rip_high ;
/* MSRC001_1035 IBS Op Data Register */
unsigned int ibs_op_data1_low ;
unsigned int ibs_op_data1_high ;
/* MSRC001_1036 IBS Op Data 2 Register */
unsigned int ibs_op_data2_low ;
unsigned int ibs_op_data2_high ;
/* MSRC001_1037 IBS Op Data 3 Register */
unsigned int ibs_op_data3_low ;
unsigned int ibs_op_data3_high ;
/* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */
unsigned int ibs_dc_linear_low ;
unsigned int ibs_dc_linear_high ;
/* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */
unsigned int ibs_dc_phys_low ;
unsigned int ibs_dc_phys_high ;
} ;
/*
* unitialize the APIC for the IBS interrupts if needed on AMD Family10h +
*/
static void clear_ibs_nmi ( void ) ;
static int ibs_allowed ; /* AMD Family10h and later */
struct op_ibs_config {
unsigned long op_enabled ;
unsigned long fetch_enabled ;
unsigned long max_cnt_fetch ;
unsigned long max_cnt_op ;
unsigned long rand_en ;
unsigned long dispatched_ops ;
} ;
static struct op_ibs_config ibs_config ;
2008-02-20 01:51:27 +03:00
2008-07-22 23:09:06 +04:00
# endif
2008-07-22 23:08:50 +04:00
/* functions for op_amd_spec */
2008-07-22 23:08:49 +04:00
2008-07-22 23:08:50 +04:00
static void op_amd_fill_in_addresses ( struct op_msrs * const msrs )
2005-04-17 02:20:36 +04:00
{
2006-09-26 12:52:26 +04:00
int i ;
2008-02-20 01:51:27 +03:00
for ( i = 0 ; i < NUM_COUNTERS ; i + + ) {
2008-09-24 13:08:52 +04:00
if ( reserve_perfctr_nmi ( MSR_K7_PERFCTR0 + i ) )
msrs - > counters [ i ] . addr = MSR_K7_PERFCTR0 + i ;
2006-09-26 12:52:26 +04:00
else
msrs - > counters [ i ] . addr = 0 ;
}
2008-02-20 01:51:27 +03:00
for ( i = 0 ; i < NUM_CONTROLS ; i + + ) {
2008-09-24 13:08:52 +04:00
if ( reserve_evntsel_nmi ( MSR_K7_EVNTSEL0 + i ) )
msrs - > controls [ i ] . addr = MSR_K7_EVNTSEL0 + i ;
2006-09-26 12:52:26 +04:00
else
msrs - > controls [ i ] . addr = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-20 01:51:27 +03:00
2008-07-22 23:08:50 +04:00
static void op_amd_setup_ctrs ( struct op_msrs const * const msrs )
2005-04-17 02:20:36 +04:00
{
unsigned int low , high ;
int i ;
2008-02-20 01:51:27 +03:00
2005-04-17 02:20:36 +04:00
/* clear all counters */
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_CONTROLS ; + + i ) {
2008-02-20 01:51:27 +03:00
if ( unlikely ( ! CTRL_IS_RESERVED ( msrs , i ) ) )
2006-09-26 12:52:26 +04:00
continue ;
2005-04-17 02:20:36 +04:00
CTRL_READ ( low , high , msrs , i ) ;
2007-12-18 20:05:58 +03:00
CTRL_CLEAR_LO ( low ) ;
CTRL_CLEAR_HI ( high ) ;
2005-04-17 02:20:36 +04:00
CTRL_WRITE ( low , high , msrs , i ) ;
}
2006-09-26 12:52:26 +04:00
2005-04-17 02:20:36 +04:00
/* avoid a false detection of ctr overflows in NMI handler */
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2008-02-20 01:51:27 +03:00
if ( unlikely ( ! CTR_IS_RESERVED ( msrs , i ) ) )
2006-09-26 12:52:26 +04:00
continue ;
2005-04-17 02:20:36 +04:00
CTR_WRITE ( 1 , msrs , i ) ;
}
/* enable active counters */
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
if ( ( counter_config [ i ] . enabled ) & & ( CTR_IS_RESERVED ( msrs , i ) ) ) {
reset_value [ i ] = counter_config [ i ] . count ;
CTR_WRITE ( counter_config [ i ] . count , msrs , i ) ;
2005-04-17 02:20:36 +04:00
CTRL_READ ( low , high , msrs , i ) ;
2007-12-18 20:05:58 +03:00
CTRL_CLEAR_LO ( low ) ;
CTRL_CLEAR_HI ( high ) ;
2005-04-17 02:20:36 +04:00
CTRL_SET_ENABLE ( low ) ;
2008-09-24 13:08:52 +04:00
CTRL_SET_USR ( low , counter_config [ i ] . user ) ;
CTRL_SET_KERN ( low , counter_config [ i ] . kernel ) ;
CTRL_SET_UM ( low , counter_config [ i ] . unit_mask ) ;
CTRL_SET_EVENT_LOW ( low , counter_config [ i ] . event ) ;
CTRL_SET_EVENT_HIGH ( high , counter_config [ i ] . event ) ;
2007-12-18 20:05:58 +03:00
CTRL_SET_HOST_ONLY ( high , 0 ) ;
CTRL_SET_GUEST_ONLY ( high , 0 ) ;
2005-04-17 02:20:36 +04:00
CTRL_WRITE ( low , high , msrs , i ) ;
2008-09-24 13:08:52 +04:00
} else {
reset_value [ i ] = 0 ;
2005-04-17 02:20:36 +04:00
}
}
}
2008-07-22 23:09:06 +04:00
# ifdef CONFIG_OPROFILE_IBS
2008-07-22 23:08:56 +04:00
static inline int
op_amd_handle_ibs ( struct pt_regs * const regs ,
struct op_msrs const * const msrs )
2005-04-17 02:20:36 +04:00
{
unsigned int low , high ;
2008-07-22 23:08:55 +04:00
struct ibs_fetch_sample ibs_fetch ;
struct ibs_op_sample ibs_op ;
2005-04-17 02:20:36 +04:00
2008-07-22 23:08:56 +04:00
if ( ! ibs_allowed )
return 1 ;
2005-04-17 02:20:36 +04:00
2008-07-22 23:08:56 +04:00
if ( ibs_config . fetch_enabled ) {
2008-07-22 23:08:55 +04:00
rdmsr ( MSR_AMD64_IBSFETCHCTL , low , high ) ;
2008-07-22 23:09:03 +04:00
if ( high & IBS_FETCH_HIGH_VALID_BIT ) {
2008-07-22 23:08:55 +04:00
ibs_fetch . ibs_fetch_ctl_high = high ;
ibs_fetch . ibs_fetch_ctl_low = low ;
rdmsr ( MSR_AMD64_IBSFETCHLINAD , low , high ) ;
ibs_fetch . ibs_fetch_lin_addr_high = high ;
ibs_fetch . ibs_fetch_lin_addr_low = low ;
rdmsr ( MSR_AMD64_IBSFETCHPHYSAD , low , high ) ;
ibs_fetch . ibs_fetch_phys_addr_high = high ;
ibs_fetch . ibs_fetch_phys_addr_low = low ;
oprofile_add_ibs_sample ( regs ,
( unsigned int * ) & ibs_fetch ,
IBS_FETCH_BEGIN ) ;
/*reenable the IRQ */
rdmsr ( MSR_AMD64_IBSFETCHCTL , low , high ) ;
2008-07-22 23:09:03 +04:00
high & = ~ IBS_FETCH_HIGH_VALID_BIT ;
high | = IBS_FETCH_HIGH_ENABLE ;
low & = IBS_FETCH_LOW_MAX_CNT_MASK ;
2008-07-22 23:08:55 +04:00
wrmsr ( MSR_AMD64_IBSFETCHCTL , low , high ) ;
}
}
2008-07-22 23:08:56 +04:00
if ( ibs_config . op_enabled ) {
2008-07-22 23:08:55 +04:00
rdmsr ( MSR_AMD64_IBSOPCTL , low , high ) ;
2008-07-22 23:09:03 +04:00
if ( low & IBS_OP_LOW_VALID_BIT ) {
2008-07-22 23:08:55 +04:00
rdmsr ( MSR_AMD64_IBSOPRIP , low , high ) ;
ibs_op . ibs_op_rip_low = low ;
ibs_op . ibs_op_rip_high = high ;
rdmsr ( MSR_AMD64_IBSOPDATA , low , high ) ;
ibs_op . ibs_op_data1_low = low ;
ibs_op . ibs_op_data1_high = high ;
rdmsr ( MSR_AMD64_IBSOPDATA2 , low , high ) ;
ibs_op . ibs_op_data2_low = low ;
ibs_op . ibs_op_data2_high = high ;
rdmsr ( MSR_AMD64_IBSOPDATA3 , low , high ) ;
ibs_op . ibs_op_data3_low = low ;
ibs_op . ibs_op_data3_high = high ;
rdmsr ( MSR_AMD64_IBSDCLINAD , low , high ) ;
ibs_op . ibs_dc_linear_low = low ;
ibs_op . ibs_dc_linear_high = high ;
rdmsr ( MSR_AMD64_IBSDCPHYSAD , low , high ) ;
ibs_op . ibs_dc_phys_low = low ;
ibs_op . ibs_dc_phys_high = high ;
/* reenable the IRQ */
oprofile_add_ibs_sample ( regs ,
( unsigned int * ) & ibs_op ,
IBS_OP_BEGIN ) ;
rdmsr ( MSR_AMD64_IBSOPCTL , low , high ) ;
2008-07-22 23:09:04 +04:00
high = 0 ;
2008-07-22 23:09:03 +04:00
low & = ~ IBS_OP_LOW_VALID_BIT ;
low | = IBS_OP_LOW_ENABLE ;
2008-07-22 23:08:55 +04:00
wrmsr ( MSR_AMD64_IBSOPCTL , low , high ) ;
}
}
2005-04-17 02:20:36 +04:00
return 1 ;
}
2008-07-22 23:09:06 +04:00
# endif
2008-07-22 23:08:56 +04:00
static int op_amd_check_ctrs ( struct pt_regs * const regs ,
struct op_msrs const * const msrs )
{
unsigned int low , high ;
int i ;
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
if ( ! reset_value [ i ] )
2008-07-22 23:08:56 +04:00
continue ;
CTR_READ ( low , high , msrs , i ) ;
if ( CTR_OVERFLOWED ( low ) ) {
2008-09-24 13:08:52 +04:00
oprofile_add_sample ( regs , i ) ;
CTR_WRITE ( reset_value [ i ] , msrs , i ) ;
2008-07-22 23:08:56 +04:00
}
}
2008-07-22 23:09:06 +04:00
# ifdef CONFIG_OPROFILE_IBS
2008-07-22 23:08:56 +04:00
op_amd_handle_ibs ( regs , msrs ) ;
2008-07-22 23:09:06 +04:00
# endif
2008-07-22 23:08:56 +04:00
/* See op_model_ppro.c */
return 1 ;
}
2008-02-20 01:51:27 +03:00
2008-07-22 23:08:50 +04:00
static void op_amd_start ( struct op_msrs const * const msrs )
2005-04-17 02:20:36 +04:00
{
unsigned int low , high ;
int i ;
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
if ( reset_value [ i ] ) {
2005-04-17 02:20:36 +04:00
CTRL_READ ( low , high , msrs , i ) ;
CTRL_SET_ACTIVE ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
}
2008-07-22 23:09:06 +04:00
# ifdef CONFIG_OPROFILE_IBS
2008-07-22 23:08:55 +04:00
if ( ibs_allowed & & ibs_config . fetch_enabled ) {
low = ( ibs_config . max_cnt_fetch > > 4 ) & 0xFFFF ;
2008-10-15 17:15:51 +04:00
high = ( ( ibs_config . rand_en & 0x1 ) < < 25 ) /* bit 57 */
+ IBS_FETCH_HIGH_ENABLE ;
2008-07-22 23:08:55 +04:00
wrmsr ( MSR_AMD64_IBSFETCHCTL , low , high ) ;
}
if ( ibs_allowed & & ibs_config . op_enabled ) {
2008-10-15 17:15:51 +04:00
low = ( ( ibs_config . max_cnt_op > > 4 ) & 0xFFFF )
+ ( ( ibs_config . dispatched_ops & 0x1 ) < < 19 ) /* bit 19 */
+ IBS_OP_LOW_ENABLE ;
2008-07-22 23:08:55 +04:00
high = 0 ;
wrmsr ( MSR_AMD64_IBSOPCTL , low , high ) ;
}
2008-07-22 23:09:06 +04:00
# endif
2005-04-17 02:20:36 +04:00
}
2008-07-22 23:08:50 +04:00
static void op_amd_stop ( struct op_msrs const * const msrs )
2005-04-17 02:20:36 +04:00
{
2008-02-20 01:51:27 +03:00
unsigned int low , high ;
2005-04-17 02:20:36 +04:00
int i ;
/* Subtle: stop on all counters to avoid race with
* setting our pm callback */
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
if ( ! reset_value [ i ] )
2006-09-26 12:52:26 +04:00
continue ;
2005-04-17 02:20:36 +04:00
CTRL_READ ( low , high , msrs , i ) ;
CTRL_SET_INACTIVE ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
2008-07-22 23:08:55 +04:00
2008-07-22 23:09:06 +04:00
# ifdef CONFIG_OPROFILE_IBS
2008-07-22 23:08:55 +04:00
if ( ibs_allowed & & ibs_config . fetch_enabled ) {
low = 0 ; /* clear max count and enable */
high = 0 ;
wrmsr ( MSR_AMD64_IBSFETCHCTL , low , high ) ;
}
if ( ibs_allowed & & ibs_config . op_enabled ) {
low = 0 ; /* clear max count and enable */
high = 0 ;
wrmsr ( MSR_AMD64_IBSOPCTL , low , high ) ;
}
2008-07-22 23:09:06 +04:00
# endif
2005-04-17 02:20:36 +04:00
}
2008-07-22 23:08:50 +04:00
static void op_amd_shutdown ( struct op_msrs const * const msrs )
2006-09-26 12:52:26 +04:00
{
int i ;
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2008-02-20 01:51:27 +03:00
if ( CTR_IS_RESERVED ( msrs , i ) )
2006-09-26 12:52:26 +04:00
release_perfctr_nmi ( MSR_K7_PERFCTR0 + i ) ;
}
2008-09-24 13:08:52 +04:00
for ( i = 0 ; i < NUM_CONTROLS ; + + i ) {
2008-02-20 01:51:27 +03:00
if ( CTRL_IS_RESERVED ( msrs , i ) )
2006-09-26 12:52:26 +04:00
release_evntsel_nmi ( MSR_K7_EVNTSEL0 + i ) ;
}
}
2005-04-17 02:20:36 +04:00
2008-07-22 23:09:06 +04:00
# ifndef CONFIG_OPROFILE_IBS
2008-07-22 23:09:02 +04:00
/* no IBS support */
static int op_amd_init ( struct oprofile_operations * ops )
{
return 0 ;
}
static void op_amd_exit ( void ) { }
# else
2008-07-22 23:08:57 +04:00
static u8 ibs_eilvt_off ;
2008-07-22 23:08:55 +04:00
static inline void apic_init_ibs_nmi_per_cpu ( void * arg )
{
2008-07-22 23:08:57 +04:00
ibs_eilvt_off = setup_APIC_eilvt_ibs ( 0 , APIC_EILVT_MSG_NMI , 0 ) ;
2008-07-22 23:08:55 +04:00
}
static inline void apic_clear_ibs_nmi_per_cpu ( void * arg )
{
setup_APIC_eilvt_ibs ( 0 , APIC_EILVT_MSG_FIX , 1 ) ;
}
2008-07-22 23:08:57 +04:00
static int pfm_amd64_setup_eilvt ( void )
{
# define IBSCTL_LVTOFFSETVAL (1 << 8)
# define IBSCTL 0x1cc
struct pci_dev * cpu_cfg ;
int nodes ;
u32 value = 0 ;
/* per CPU setup */
2008-07-22 23:08:59 +04:00
on_each_cpu ( apic_init_ibs_nmi_per_cpu , NULL , 1 ) ;
2008-07-22 23:08:57 +04:00
nodes = 0 ;
cpu_cfg = NULL ;
do {
cpu_cfg = pci_get_device ( PCI_VENDOR_ID_AMD ,
PCI_DEVICE_ID_AMD_10H_NB_MISC ,
cpu_cfg ) ;
if ( ! cpu_cfg )
break ;
+ + nodes ;
pci_write_config_dword ( cpu_cfg , IBSCTL , ibs_eilvt_off
| IBSCTL_LVTOFFSETVAL ) ;
pci_read_config_dword ( cpu_cfg , IBSCTL , & value ) ;
if ( value ! = ( ibs_eilvt_off | IBSCTL_LVTOFFSETVAL ) ) {
printk ( KERN_DEBUG " Failed to setup IBS LVT offset, "
" IBSCTL = 0x%08x " , value ) ;
return 1 ;
}
} while ( 1 ) ;
if ( ! nodes ) {
printk ( KERN_DEBUG " No CPU node configured for IBS " ) ;
return 1 ;
}
# ifdef CONFIG_NUMA
/* Sanity check */
/* Works only for 64bit with proper numa implementation. */
if ( nodes ! = num_possible_nodes ( ) ) {
printk ( KERN_DEBUG " Failed to setup CPU node(s) for IBS, "
" found: %d, expected %d " ,
nodes , num_possible_nodes ( ) ) ;
return 1 ;
}
# endif
return 0 ;
}
2008-07-22 23:08:55 +04:00
/*
* initialize the APIC for the IBS interrupts
2008-07-22 23:08:57 +04:00
* if available ( AMD Family10h rev B0 and later )
2008-07-22 23:08:55 +04:00
*/
static void setup_ibs ( void )
{
ibs_allowed = boot_cpu_has ( X86_FEATURE_IBS ) ;
if ( ! ibs_allowed )
return ;
2008-07-22 23:09:06 +04:00
if ( pfm_amd64_setup_eilvt ( ) ) {
2008-07-22 23:08:57 +04:00
ibs_allowed = 0 ;
2008-07-22 23:09:06 +04:00
return ;
}
printk ( KERN_INFO " oprofile: AMD IBS detected \n " ) ;
2008-07-22 23:08:55 +04:00
}
/*
* unitialize the APIC for the IBS interrupts if needed on AMD Family10h
* rev B0 and later */
static void clear_ibs_nmi ( void )
{
if ( ibs_allowed )
2008-07-22 23:08:59 +04:00
on_each_cpu ( apic_clear_ibs_nmi_per_cpu , NULL , 1 ) ;
2008-07-22 23:08:55 +04:00
}
2008-09-05 19:12:36 +04:00
static int ( * create_arch_files ) ( struct super_block * sb , struct dentry * root ) ;
2008-07-22 23:09:01 +04:00
2008-09-05 19:12:36 +04:00
static int setup_ibs_files ( struct super_block * sb , struct dentry * root )
2008-07-22 23:08:55 +04:00
{
struct dentry * dir ;
2008-07-22 23:09:01 +04:00
int ret = 0 ;
/* architecture specific files */
if ( create_arch_files )
ret = create_arch_files ( sb , root ) ;
if ( ret )
return ret ;
2008-07-22 23:08:55 +04:00
if ( ! ibs_allowed )
2008-07-22 23:09:01 +04:00
return ret ;
/* model specific files */
2008-07-22 23:08:55 +04:00
/* setup some reasonable defaults */
ibs_config . max_cnt_fetch = 250000 ;
ibs_config . fetch_enabled = 0 ;
ibs_config . max_cnt_op = 250000 ;
ibs_config . op_enabled = 0 ;
ibs_config . dispatched_ops = 1 ;
2008-07-18 19:56:05 +04:00
dir = oprofilefs_mkdir ( sb , root , " ibs_fetch " ) ;
2008-07-22 23:08:55 +04:00
oprofilefs_create_ulong ( sb , dir , " enable " ,
2008-07-18 19:56:05 +04:00
& ibs_config . fetch_enabled ) ;
2008-07-22 23:08:55 +04:00
oprofilefs_create_ulong ( sb , dir , " max_count " ,
2008-07-18 19:56:05 +04:00
& ibs_config . max_cnt_fetch ) ;
oprofilefs_create_ulong ( sb , dir , " rand_enable " ,
& ibs_config . rand_en ) ;
2008-07-29 18:57:10 +04:00
dir = oprofilefs_mkdir ( sb , root , " ibs_op " ) ;
2008-07-22 23:08:55 +04:00
oprofilefs_create_ulong ( sb , dir , " enable " ,
2008-07-18 19:56:05 +04:00
& ibs_config . op_enabled ) ;
2008-07-22 23:08:55 +04:00
oprofilefs_create_ulong ( sb , dir , " max_count " ,
2008-07-18 19:56:05 +04:00
& ibs_config . max_cnt_op ) ;
2008-07-22 23:08:55 +04:00
oprofilefs_create_ulong ( sb , dir , " dispatched_ops " ,
2008-07-18 19:56:05 +04:00
& ibs_config . dispatched_ops ) ;
2008-07-22 23:09:00 +04:00
return 0 ;
2008-07-22 23:08:55 +04:00
}
2008-07-22 23:08:48 +04:00
static int op_amd_init ( struct oprofile_operations * ops )
{
2008-07-22 23:09:01 +04:00
setup_ibs ( ) ;
create_arch_files = ops - > create_files ;
ops - > create_files = setup_ibs_files ;
2008-07-22 23:08:48 +04:00
return 0 ;
}
static void op_amd_exit ( void )
{
2008-07-22 23:09:01 +04:00
clear_ibs_nmi ( ) ;
2008-07-22 23:08:48 +04:00
}
2008-07-22 23:09:02 +04:00
# endif
2008-07-22 23:08:50 +04:00
struct op_x86_model_spec const op_amd_spec = {
2008-09-05 19:12:36 +04:00
. init = op_amd_init ,
. exit = op_amd_exit ,
. num_counters = NUM_COUNTERS ,
. num_controls = NUM_CONTROLS ,
. fill_in_addresses = & op_amd_fill_in_addresses ,
. setup_ctrs = & op_amd_setup_ctrs ,
. check_ctrs = & op_amd_check_ctrs ,
. start = & op_amd_start ,
. stop = & op_amd_stop ,
. shutdown = & op_amd_shutdown
2005-04-17 02:20:36 +04:00
} ;