2008-02-20 01:43:25 +03:00
/*
2005-04-17 02:20:36 +04:00
* @ file op_model_ppro . h
2008-08-18 16:50:31 +04:00
* Family 6 perfmon and architectural perfmon MSR operations
2005-04-17 02:20:36 +04:00
*
* @ remark Copyright 2002 OProfile authors
2008-08-18 16:50:31 +04:00
* @ remark Copyright 2008 Intel Corporation
2005-04-17 02:20:36 +04:00
* @ remark Read the file COPYING
*
* @ author John Levon
* @ author Philippe Elie
* @ author Graydon Hoare
2008-08-18 16:50:31 +04:00
* @ author Andi Kleen
2005-04-17 02:20:36 +04:00
*/
# include <linux/oprofile.h>
2008-08-18 16:50:31 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <asm/ptrace.h>
# include <asm/msr.h>
# include <asm/apic.h>
2006-06-26 15:57:01 +04:00
# include <asm/nmi.h>
2008-08-18 16:50:31 +04:00
# include <asm/intel_arch_perfmon.h>
2008-02-20 01:43:25 +03:00
2005-04-17 02:20:36 +04:00
# include "op_x86_model.h"
# include "op_counter.h"
2008-08-18 16:50:31 +04:00
static int num_counters = 2 ;
static int counter_width = 32 ;
2005-04-17 02:20:36 +04:00
2008-02-20 01:43:25 +03:00
# define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
2008-11-07 16:02:49 +03:00
# define CTR_OVERFLOWED(n) (!((n) & (1ULL<<(counter_width-1))))
2005-04-17 02:20:36 +04:00
2008-02-20 01:43:25 +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))
# define CTRL_CLEAR(x) (x &= (1<<21))
# define CTRL_SET_ENABLE(val) (val |= 1<<20)
2008-02-20 01:43:25 +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))
# define CTRL_SET_EVENT(val, e) (val |= e)
2008-08-18 16:50:31 +04:00
static u64 * reset_value ;
2008-02-20 01:43:25 +03:00
2005-04-17 02:20:36 +04:00
static void ppro_fill_in_addresses ( struct op_msrs * const msrs )
{
2006-09-26 12:52:26 +04:00
int i ;
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; i + + ) {
2006-09-26 12:52:26 +04:00
if ( reserve_perfctr_nmi ( MSR_P6_PERFCTR0 + i ) )
msrs - > counters [ i ] . addr = MSR_P6_PERFCTR0 + i ;
else
msrs - > counters [ i ] . addr = 0 ;
}
2008-02-20 01:43:25 +03:00
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; i + + ) {
2006-09-26 12:52:26 +04:00
if ( reserve_evntsel_nmi ( MSR_P6_EVNTSEL0 + i ) )
msrs - > controls [ i ] . addr = MSR_P6_EVNTSEL0 + i ;
else
msrs - > controls [ i ] . addr = 0 ;
}
2005-04-17 02:20:36 +04:00
}
static void ppro_setup_ctrs ( struct op_msrs const * const msrs )
{
unsigned int low , high ;
int i ;
2008-08-18 16:50:31 +04:00
if ( ! reset_value ) {
2008-11-10 11:05:37 +03:00
reset_value = kmalloc ( sizeof ( reset_value [ 0 ] ) * num_counters ,
2008-08-18 16:50:31 +04:00
GFP_ATOMIC ) ;
if ( ! reset_value )
return ;
}
if ( cpu_has_arch_perfmon ) {
union cpuid10_eax eax ;
eax . full = cpuid_eax ( 0xa ) ;
if ( counter_width < eax . split . bit_width )
counter_width = eax . split . bit_width ;
}
2005-04-17 02:20:36 +04:00
/* clear all counters */
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2008-02-20 01:43:25 +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 ) ;
CTRL_CLEAR ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
2008-02-20 01:43:25 +03:00
2005-04-17 02:20:36 +04:00
/* avoid a false detection of ctr overflows in NMI handler */
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2008-02-20 01:43:25 +03:00
if ( unlikely ( ! CTR_IS_RESERVED ( msrs , i ) ) )
2006-09-26 12:52:26 +04:00
continue ;
2008-08-18 16:50:31 +04:00
wrmsrl ( msrs - > counters [ i ] . addr , - 1LL ) ;
2005-04-17 02:20:36 +04:00
}
/* enable active counters */
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2008-02-20 01:43:25 +03:00
if ( ( counter_config [ i ] . enabled ) & & ( CTR_IS_RESERVED ( msrs , i ) ) ) {
2005-04-17 02:20:36 +04:00
reset_value [ i ] = counter_config [ i ] . count ;
2008-08-18 16:50:31 +04:00
wrmsrl ( msrs - > counters [ i ] . addr , - reset_value [ i ] ) ;
2005-04-17 02:20:36 +04:00
CTRL_READ ( low , high , msrs , i ) ;
CTRL_CLEAR ( low ) ;
CTRL_SET_ENABLE ( low ) ;
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 , counter_config [ i ] . event ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
2006-09-26 12:52:26 +04:00
} else {
reset_value [ i ] = 0 ;
2005-04-17 02:20:36 +04:00
}
}
}
2008-02-20 01:43:25 +03:00
2005-04-17 02:20:36 +04:00
static int ppro_check_ctrs ( struct pt_regs * const regs ,
struct op_msrs const * const msrs )
{
2008-11-07 16:02:49 +03:00
u64 val ;
2005-04-17 02:20:36 +04:00
int i ;
2008-02-20 01:43:25 +03:00
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2006-09-26 12:52:26 +04:00
if ( ! reset_value [ i ] )
continue ;
2008-11-07 16:02:49 +03:00
rdmsrl ( msrs - > counters [ i ] . addr , val ) ;
if ( CTR_OVERFLOWED ( val ) ) {
2005-04-17 02:20:36 +04:00
oprofile_add_sample ( regs , i ) ;
2008-08-18 16:50:31 +04:00
wrmsrl ( msrs - > counters [ i ] . addr , - reset_value [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
}
/* Only P6 based Pentium M need to re-unmask the apic vector but it
* doesn ' t hurt other P6 variant */
apic_write ( APIC_LVTPC , apic_read ( APIC_LVTPC ) & ~ APIC_LVT_MASKED ) ;
/* We can't work out if we really handled an interrupt. We
* might have caught a * second * counter just after overflowing
* the interrupt for this counter then arrives
* and we don ' t find a counter that ' s overflowed , so we
* would return 0 and get dazed + confused . Instead we always
* assume we found an overflow . This sucks .
*/
return 1 ;
}
2008-02-20 01:43:25 +03:00
2005-04-17 02:20:36 +04:00
static void ppro_start ( struct op_msrs const * const msrs )
{
2008-02-20 01:43:25 +03:00
unsigned int low , high ;
2006-09-29 13:00:01 +04:00
int i ;
2006-09-26 12:52:26 +04:00
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2006-09-29 13:00:01 +04:00
if ( reset_value [ i ] ) {
CTRL_READ ( low , high , msrs , i ) ;
CTRL_SET_ACTIVE ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
2006-09-26 12:52:26 +04:00
}
2005-04-17 02:20:36 +04:00
}
static void ppro_stop ( struct op_msrs const * const msrs )
{
2008-02-20 01:43:25 +03:00
unsigned int low , high ;
2006-09-29 13:00:01 +04:00
int i ;
2006-09-26 12:52:26 +04:00
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2006-09-29 13:00:01 +04:00
if ( ! reset_value [ i ] )
continue ;
CTRL_READ ( low , high , msrs , i ) ;
2006-09-26 12:52:26 +04:00
CTRL_SET_INACTIVE ( low ) ;
2006-09-29 13:00:01 +04:00
CTRL_WRITE ( low , high , msrs , i ) ;
2006-09-26 12:52:26 +04:00
}
}
static void ppro_shutdown ( struct op_msrs const * const msrs )
{
int i ;
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2008-02-20 01:43:25 +03:00
if ( CTR_IS_RESERVED ( msrs , i ) )
2006-09-26 12:52:26 +04:00
release_perfctr_nmi ( MSR_P6_PERFCTR0 + i ) ;
}
2008-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2008-02-20 01:43:25 +03:00
if ( CTRL_IS_RESERVED ( msrs , i ) )
2006-09-26 12:52:26 +04:00
release_evntsel_nmi ( MSR_P6_EVNTSEL0 + i ) ;
}
2008-08-18 16:50:31 +04:00
if ( reset_value ) {
kfree ( reset_value ) ;
reset_value = NULL ;
}
2005-04-17 02:20:36 +04:00
}
2008-09-30 00:23:33 +04:00
struct op_x86_model_spec op_ppro_spec = {
2008-10-16 00:19:41 +04:00
. num_counters = 2 , /* can be overriden */
. num_controls = 2 , /* dito */
. fill_in_addresses = & ppro_fill_in_addresses ,
. setup_ctrs = & ppro_setup_ctrs ,
. check_ctrs = & ppro_check_ctrs ,
. start = & ppro_start ,
. stop = & ppro_stop ,
. shutdown = & ppro_shutdown
2008-08-18 16:50:31 +04:00
} ;
/*
* Architectural performance monitoring .
*
* Newer Intel CPUs ( Core1 + ) have support for architectural
* events described in CPUID 0xA . See the IA32 SDM Vol3b .18 for details .
* The advantage of this is that it can be done without knowing about
* the specific CPU .
*/
void arch_perfmon_setup_counters ( void )
{
union cpuid10_eax eax ;
eax . full = cpuid_eax ( 0xa ) ;
/* Workaround for BIOS bugs in 6/15. Taken from perfmon2 */
if ( eax . split . version_id = = 0 & & current_cpu_data . x86 = = 6 & &
current_cpu_data . x86_model = = 15 ) {
eax . split . version_id = 2 ;
eax . split . num_counters = 2 ;
eax . split . bit_width = 40 ;
}
num_counters = eax . split . num_counters ;
op_arch_perfmon_spec . num_counters = num_counters ;
op_arch_perfmon_spec . num_controls = num_counters ;
2008-09-30 00:23:33 +04:00
op_ppro_spec . num_counters = num_counters ;
op_ppro_spec . num_controls = num_counters ;
2008-08-18 16:50:31 +04:00
}
struct op_x86_model_spec op_arch_perfmon_spec = {
/* num_counters/num_controls filled in at runtime */
2008-09-05 19:12:36 +04:00
. fill_in_addresses = & ppro_fill_in_addresses ,
2008-08-18 16:50:31 +04:00
/* user space does the cpuid check for available events */
2008-09-05 19:12:36 +04:00
. setup_ctrs = & ppro_setup_ctrs ,
. check_ctrs = & ppro_check_ctrs ,
. start = & ppro_start ,
. stop = & ppro_stop ,
. shutdown = & ppro_shutdown
2005-04-17 02:20:36 +04:00
} ;