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
2009-05-25 17:10:32 +04:00
* @ author Robert Richter < robert . richter @ amd . com >
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-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
2009-05-25 17:10:32 +04:00
# define MSR_PPRO_EVENTSEL_RESERVED ((0xFFFFFFFFULL<<32)|(1ULL<<21))
2005-04-17 02:20:36 +04:00
2011-08-01 19:08:59 +04:00
static u64 reset_value [ OP_MAX_COUNTER ] ;
2008-02-20 01:43:25 +03:00
2010-03-23 22:01:54 +03:00
static void ppro_shutdown ( struct op_msrs const * const msrs )
{
int i ;
for ( i = 0 ; i < num_counters ; + + i ) {
if ( ! msrs - > counters [ i ] . addr )
continue ;
release_perfctr_nmi ( MSR_P6_PERFCTR0 + i ) ;
release_evntsel_nmi ( MSR_P6_EVNTSEL0 + i ) ;
}
}
2010-02-26 19:20:55 +03:00
static int ppro_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-08-18 16:50:31 +04:00
for ( i = 0 ; i < num_counters ; i + + ) {
2010-03-23 21:33:21 +03:00
if ( ! reserve_perfctr_nmi ( MSR_P6_PERFCTR0 + i ) )
2010-02-26 19:20:55 +03:00
goto fail ;
2010-03-23 21:33:21 +03:00
if ( ! reserve_evntsel_nmi ( MSR_P6_EVNTSEL0 + i ) ) {
release_perfctr_nmi ( MSR_P6_PERFCTR0 + i ) ;
2010-02-26 19:20:55 +03:00
goto fail ;
2010-03-23 21:33:21 +03:00
}
/* both registers must be reserved */
msrs - > counters [ i ] . addr = MSR_P6_PERFCTR0 + i ;
msrs - > controls [ i ] . addr = MSR_P6_EVNTSEL0 + i ;
2010-02-26 19:20:55 +03:00
continue ;
fail :
if ( ! counter_config [ i ] . enabled )
continue ;
op_x86_warn_reserved ( i ) ;
ppro_shutdown ( msrs ) ;
return - EBUSY ;
2006-09-26 12:52:26 +04:00
}
2010-02-26 19:20:55 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-05-25 21:31:44 +04:00
static void ppro_setup_ctrs ( struct op_x86_model_spec const * model ,
struct op_msrs const * const msrs )
2005-04-17 02:20:36 +04:00
{
2009-05-25 17:10:32 +04:00
u64 val ;
2005-04-17 02:20:36 +04:00
int i ;
2008-08-18 16:50:31 +04:00
if ( cpu_has_arch_perfmon ) {
union cpuid10_eax eax ;
eax . full = cpuid_eax ( 0xa ) ;
2009-02-19 19:34:03 +03:00
/*
* For Core2 ( family 6 , model 15 ) , don ' t reset the
* counter width :
*/
if ( ! ( eax . split . version_id = = 0 & &
2010-12-18 18:30:05 +03:00
__this_cpu_read ( cpu_info . x86 ) = = 6 & &
__this_cpu_read ( cpu_info . x86_model ) = = 15 ) ) {
2009-02-19 19:34:03 +03:00
if ( counter_width < eax . split . bit_width )
counter_width = eax . split . bit_width ;
}
2008-08-18 16:50:31 +04:00
}
2005-04-17 02:20:36 +04:00
/* clear all counters */
2009-07-07 21:25:39 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
2010-02-26 19:20:55 +03:00
if ( ! msrs - > controls [ i ] . addr )
2006-09-26 12:52:26 +04:00
continue ;
2009-05-25 17:10:32 +04:00
rdmsrl ( msrs - > controls [ i ] . addr , val ) ;
2010-03-01 16:21:23 +03:00
if ( val & ARCH_PERFMON_EVENTSEL_ENABLE )
2010-02-23 20:14:58 +03:00
op_x86_warn_in_use ( i ) ;
2009-05-25 17:10:32 +04:00
val & = model - > reserved ;
wrmsrl ( msrs - > controls [ i ] . addr , val ) ;
2010-03-23 21:33:21 +03:00
/*
* avoid a false detection of ctr overflows in NMI *
* handler
*/
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 ) {
2009-06-04 04:36:44 +04:00
if ( counter_config [ i ] . enabled & & msrs - > counters [ i ] . addr ) {
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 ] ) ;
2009-05-25 17:10:32 +04:00
rdmsrl ( msrs - > controls [ i ] . addr , val ) ;
val & = model - > reserved ;
val | = op_x86_get_ctrl ( model , & counter_config [ i ] ) ;
wrmsrl ( msrs - > controls [ i ] . addr , val ) ;
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
2009-07-07 21:25:39 +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 ) ;
2009-05-25 19:59:06 +04:00
if ( val & ( 1ULL < < ( counter_width - 1 ) ) )
continue ;
oprofile_add_sample ( regs , i ) ;
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 )
{
2009-05-25 20:11:52 +04:00
u64 val ;
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 ] ) {
2009-05-25 20:11:52 +04:00
rdmsrl ( msrs - > controls [ i ] . addr , val ) ;
2010-03-01 16:21:23 +03:00
val | = ARCH_PERFMON_EVENTSEL_ENABLE ;
2009-05-25 20:11:52 +04:00
wrmsrl ( msrs - > controls [ i ] . addr , val ) ;
2006-09-29 13:00:01 +04:00
}
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 )
{
2009-05-25 20:11:52 +04:00
u64 val ;
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 ;
2009-05-25 20:11:52 +04:00
rdmsrl ( msrs - > controls [ i ] . addr , val ) ;
2010-03-01 16:21:23 +03:00
val & = ~ ARCH_PERFMON_EVENTSEL_ENABLE ;
2009-05-25 20:11:52 +04:00
wrmsrl ( msrs - > controls [ i ] . addr , val ) ;
2006-09-26 12:52:26 +04:00
}
}
2009-07-09 17:12:35 +04:00
struct op_x86_model_spec op_ppro_spec = {
2009-05-14 19:10:52 +04:00
. num_counters = 2 ,
. num_controls = 2 ,
2009-05-25 17:10:32 +04:00
. reserved = MSR_PPRO_EVENTSEL_RESERVED ,
2008-10-16 00:19:41 +04:00
. 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 .
*/
2008-10-12 23:12:34 +04:00
static void arch_perfmon_setup_counters ( void )
2008-08-18 16:50:31 +04:00
{
union cpuid10_eax eax ;
eax . full = cpuid_eax ( 0xa ) ;
/* Workaround for BIOS bugs in 6/15. Taken from perfmon2 */
2010-12-18 18:30:05 +03:00
if ( eax . split . version_id = = 0 & & __this_cpu_read ( cpu_info . x86 ) = = 6 & &
__this_cpu_read ( cpu_info . x86_model ) = = 15 ) {
2008-08-18 16:50:31 +04:00
eax . split . version_id = 2 ;
2010-03-29 20:36:50 +04:00
eax . split . num_counters = 2 ;
2008-08-18 16:50:31 +04:00
eax . split . bit_width = 40 ;
}
2011-08-17 01:39:53 +04:00
num_counters = min ( ( int ) eax . split . num_counters , OP_MAX_COUNTER ) ;
2008-08-18 16:50:31 +04:00
op_arch_perfmon_spec . num_counters = num_counters ;
op_arch_perfmon_spec . num_controls = num_counters ;
}
2008-10-12 23:12:34 +04:00
static int arch_perfmon_init ( struct oprofile_operations * ignore )
{
arch_perfmon_setup_counters ( ) ;
return 0 ;
}
2008-08-18 16:50:31 +04:00
struct op_x86_model_spec op_arch_perfmon_spec = {
2009-05-25 17:10:32 +04:00
. reserved = MSR_PPRO_EVENTSEL_RESERVED ,
2008-10-12 23:12:34 +04:00
. init = & arch_perfmon_init ,
2008-08-18 16:50:31 +04:00
/* 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
} ;