2008-01-30 13:31:09 +01:00
/*
* Debug Store support
*
* This provides a low - level interface to the hardware ' s Debug Store
2008-04-08 11:01:58 +02:00
* feature that is used for branch trace store ( BTS ) and
2008-01-30 13:31:09 +01:00
* precise - event based sampling ( PEBS ) .
*
2008-04-08 11:01:58 +02:00
* It manages :
2008-12-11 13:49:59 +01:00
* - DS and BTS hardware configuration
2008-11-25 09:05:27 +01:00
* - buffer overflow handling ( to be done )
2008-04-08 11:01:58 +02:00
* - buffer access
2008-01-30 13:31:09 +01:00
*
2008-12-11 13:49:59 +01:00
* It does not do :
* - security checking ( is the caller allowed to trace the task )
* - buffer allocation ( memory accounting )
2008-01-30 13:31:09 +01:00
*
*
2009-01-19 10:38:35 +01:00
* Copyright ( C ) 2007 - 2009 Intel Corporation .
* Markus Metzger < markus . t . metzger @ intel . com > , 2007 - 2009
2008-01-30 13:31:09 +01:00
*/
2009-03-13 11:54:40 +01:00
# include <linux/kernel.h>
2008-01-30 13:31:09 +01:00
# include <linux/string.h>
2009-03-13 11:54:40 +01:00
# include <linux/errno.h>
2008-04-08 11:01:58 +02:00
# include <linux/sched.h>
2009-03-13 11:54:40 +01:00
# include <linux/slab.h>
2008-07-25 11:32:36 +02:00
# include <linux/mm.h>
2009-04-03 16:43:38 +02:00
# include <linux/trace_clock.h>
2009-03-13 11:54:40 +01:00
# include <asm/ds.h>
2008-04-08 11:01:58 +02:00
2009-03-13 10:45:07 +01:00
# include "ds_selftest.h"
2008-04-08 11:01:58 +02:00
/*
2009-03-13 11:54:40 +01:00
* The configuration for a particular DS hardware implementation :
2008-04-08 11:01:58 +02:00
*/
struct ds_configuration {
2009-03-13 11:54:40 +01:00
/* The name of the configuration: */
const char * name ;
/* The size of pointer-typed fields in DS, BTS, and PEBS: */
unsigned char sizeof_ptr_field ;
/* The size of a BTS/PEBS record in bytes: */
unsigned char sizeof_rec [ 2 ] ;
2009-04-03 16:43:52 +02:00
/* The number of pebs counter reset values in the DS structure. */
unsigned char nr_counter_reset ;
2009-03-13 11:54:40 +01:00
/* Control bit-masks indexed by enum ds_feature: */
unsigned long ctl [ dsf_ctl_max ] ;
2008-04-08 11:01:58 +02:00
} ;
2009-04-03 16:43:47 +02:00
static struct ds_configuration ds_cfg __read_mostly ;
2008-12-11 13:49:59 +01:00
2009-03-13 11:54:40 +01:00
/* Maximal size of a DS configuration: */
2009-04-03 16:43:52 +02:00
# define MAX_SIZEOF_DS 0x80
2009-03-13 11:54:40 +01:00
/* Maximal size of a BTS record: */
# define MAX_SIZEOF_BTS (3 * 8)
2008-12-11 13:49:59 +01:00
2009-03-13 11:54:40 +01:00
/* BTS and PEBS buffer alignment: */
# define DS_ALIGNMENT (1 << 3)
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:52 +02:00
/* Number of buffer pointers in DS: */
# define NUM_DS_PTR_FIELDS 8
/* Size of a pebs reset value in DS: */
# define PEBS_RESET_FIELD_SIZE 8
2009-03-13 11:54:40 +01:00
/* Mask of control bits in the DS MSR register: */
# define BTS_CONTROL \
( ds_cfg . ctl [ dsf_bts ] | \
ds_cfg . ctl [ dsf_bts_kernel ] | \
ds_cfg . ctl [ dsf_bts_user ] | \
ds_cfg . ctl [ dsf_bts_overflow ] )
2008-01-30 13:31:09 +01:00
2008-11-25 09:01:25 +01:00
/*
* A BTS or PEBS tracer .
*
* This holds the configuration of the tracer and serves as a handle
* to identify tracers .
*/
struct ds_tracer {
2009-03-13 10:46:42 +01:00
/* The DS context (partially) owned by this tracer. */
2009-03-13 11:54:40 +01:00
struct ds_context * context ;
2009-03-13 10:46:42 +01:00
/* The buffer provided on ds_request() and its size in bytes. */
2009-03-13 11:54:40 +01:00
void * buffer ;
size_t size ;
2008-11-25 09:01:25 +01:00
} ;
struct bts_tracer {
2009-03-13 11:54:40 +01:00
/* The common DS part: */
struct ds_tracer ds ;
/* The trace including the DS configuration: */
struct bts_trace trace ;
/* Buffer overflow notification function: */
bts_ovfl_callback_t ovfl ;
2009-04-03 16:43:33 +02:00
/* Active flags affecting trace collection. */
unsigned int flags ;
2008-11-25 09:01:25 +01:00
} ;
struct pebs_tracer {
2009-03-13 11:54:40 +01:00
/* The common DS part: */
struct ds_tracer ds ;
/* The trace including the DS configuration: */
struct pebs_trace trace ;
/* Buffer overflow notification function: */
pebs_ovfl_callback_t ovfl ;
2008-11-25 09:01:25 +01:00
} ;
2008-01-30 13:31:09 +01:00
/*
* Debug Store ( DS ) save area configuration ( see Intel64 and IA32
* Architectures Software Developer ' s Manual , section 18.5 )
*
* The DS configuration consists of the following fields ; different
* architetures vary in the size of those fields .
2009-03-13 11:54:40 +01:00
*
2008-01-30 13:31:09 +01:00
* - double - word aligned base linear address of the BTS buffer
* - write pointer into the BTS buffer
* - end linear address of the BTS buffer ( one byte beyond the end of
* the buffer )
* - interrupt pointer into BTS buffer
* ( interrupt occurs when write pointer passes interrupt pointer )
* - double - word aligned base linear address of the PEBS buffer
* - write pointer into the PEBS buffer
* - end linear address of the PEBS buffer ( one byte beyond the end of
* the buffer )
* - interrupt pointer into PEBS buffer
* ( interrupt occurs when write pointer passes interrupt pointer )
* - value to which counter is reset following counter overflow
*
2008-04-08 11:01:58 +02:00
* Later architectures use 64 bit pointers throughout , whereas earlier
* architectures use 32 bit pointers in 32 bit mode .
2008-01-30 13:31:09 +01:00
*
*
2008-04-08 11:01:58 +02:00
* We compute the base address for the first 8 fields based on :
* - the field size stored in the DS configuration
* - the relative field position
* - an offset giving the start of the respective region
2008-01-30 13:31:09 +01:00
*
2008-04-08 11:01:58 +02:00
* This offset is further used to index various arrays holding
* information for BTS and PEBS at the respective index .
2008-01-30 13:31:09 +01:00
*
2008-04-08 11:01:58 +02:00
* On later 32 bit processors , we only access the lower 32 bit of the
* 64 bit pointer fields . The upper halves will be zeroed out .
2008-01-30 13:31:09 +01:00
*/
2008-04-08 11:01:58 +02:00
enum ds_field {
ds_buffer_base = 0 ,
ds_index ,
ds_absolute_maximum ,
ds_interrupt_threshold ,
} ;
2008-01-30 13:31:09 +01:00
2008-04-08 11:01:58 +02:00
enum ds_qualifier {
2009-03-13 11:54:40 +01:00
ds_bts = 0 ,
2008-04-08 11:01:58 +02:00
ds_pebs
2008-01-30 13:31:09 +01:00
} ;
2009-03-13 11:54:40 +01:00
static inline unsigned long
ds_get ( const unsigned char * base , enum ds_qualifier qual , enum ds_field field )
2008-04-08 11:01:58 +02:00
{
2009-03-13 10:42:18 +01:00
base + = ( ds_cfg . sizeof_ptr_field * ( field + ( 4 * qual ) ) ) ;
2008-04-08 11:01:58 +02:00
return * ( unsigned long * ) base ;
}
2009-03-13 11:54:40 +01:00
static inline void
ds_set ( unsigned char * base , enum ds_qualifier qual , enum ds_field field ,
unsigned long value )
2008-04-08 11:01:58 +02:00
{
2009-03-13 10:42:18 +01:00
base + = ( ds_cfg . sizeof_ptr_field * ( field + ( 4 * qual ) ) ) ;
2008-04-08 11:01:58 +02:00
( * ( unsigned long * ) base ) = value ;
}
2008-01-30 13:31:09 +01:00
/*
2008-11-25 09:05:27 +01:00
* Locking is done only for allocating BTS or PEBS resources .
2008-01-30 13:31:09 +01:00
*/
2008-12-11 13:49:59 +01:00
static DEFINE_SPINLOCK ( ds_lock ) ;
2008-01-30 13:31:09 +01:00
/*
2008-04-08 11:01:58 +02:00
* We either support ( system - wide ) per - cpu or per - thread allocation .
* We distinguish the two based on the task_struct pointer , where a
* NULL pointer indicates per - cpu allocation for the current cpu .
*
* Allocations are use - counted . As soon as resources are allocated ,
* further allocations must be of the same type ( per - cpu or
* per - thread ) . We model this by counting allocations ( i . e . the number
* of tracers of a certain type ) for one type negatively :
* = 0 no tracers
* > 0 number of per - thread tracers
* < 0 number of per - cpu tracers
*
* Tracers essentially gives the number of ds contexts for a certain
* type of allocation .
2008-01-30 13:31:09 +01:00
*/
2008-12-11 13:49:59 +01:00
static atomic_t tracers = ATOMIC_INIT ( 0 ) ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:37 +02:00
static inline int get_tracer ( struct task_struct * task )
2008-01-30 13:31:09 +01:00
{
2009-04-03 16:43:37 +02:00
int error ;
spin_lock_irq ( & ds_lock ) ;
if ( task ) {
error = - EPERM ;
if ( atomic_read ( & tracers ) < 0 )
goto out ;
2008-12-11 13:49:59 +01:00
atomic_inc ( & tracers ) ;
2009-04-03 16:43:37 +02:00
} else {
error = - EPERM ;
if ( atomic_read ( & tracers ) > 0 )
goto out ;
2008-12-11 13:49:59 +01:00
atomic_dec ( & tracers ) ;
2009-04-03 16:43:37 +02:00
}
error = 0 ;
out :
spin_unlock_irq ( & ds_lock ) ;
return error ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
static inline void put_tracer ( struct task_struct * task )
2008-01-30 13:31:09 +01:00
{
2008-12-11 13:49:59 +01:00
if ( task )
atomic_dec ( & tracers ) ;
else
atomic_inc ( & tracers ) ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
/*
* The DS context is either attached to a thread or to a cpu :
* - in the former case , the thread_struct contains a pointer to the
* attached context .
* - in the latter case , we use a static array of per - cpu context
* pointers .
*
* Contexts are use - counted . They are allocated on first access and
* deallocated when the last user puts the context .
*/
2008-12-11 13:49:59 +01:00
struct ds_context {
2009-03-13 11:54:40 +01:00
/* The DS configuration; goes into MSR_IA32_DS_AREA: */
unsigned char ds [ MAX_SIZEOF_DS ] ;
/* The owner of the BTS and PEBS configuration, respectively: */
struct bts_tracer * bts_master ;
struct pebs_tracer * pebs_master ;
/* Use count: */
2009-04-03 16:43:40 +02:00
unsigned long count ;
2009-03-13 11:54:40 +01:00
/* Pointer to the context pointer field: */
struct ds_context * * this ;
2009-04-03 16:43:40 +02:00
/* The traced task; NULL for cpu tracing: */
2009-03-13 11:54:40 +01:00
struct task_struct * task ;
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:40 +02:00
/* The traced cpu; only valid if task is NULL: */
int cpu ;
} ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
static DEFINE_PER_CPU ( struct ds_context * , cpu_context ) ;
2008-04-08 11:01:58 +02:00
2008-12-16 15:51:03 +01:00
2009-04-03 16:43:40 +02:00
static struct ds_context * ds_get_context ( struct task_struct * task , int cpu )
2008-01-30 13:31:09 +01:00
{
2008-04-08 11:01:58 +02:00
struct ds_context * * p_context =
2009-04-03 16:43:40 +02:00
( task ? & task - > thread . ds_ctx : & per_cpu ( cpu_context , cpu ) ) ;
2008-12-16 15:51:03 +01:00
struct ds_context * context = NULL ;
struct ds_context * new_context = NULL ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
/* Chances are small that we already have a context. */
new_context = kzalloc ( sizeof ( * new_context ) , GFP_KERNEL ) ;
2008-12-16 15:51:03 +01:00
if ( ! new_context )
return NULL ;
2008-11-25 08:52:56 +01:00
2009-04-03 16:43:40 +02:00
spin_lock_irq ( & ds_lock ) ;
2008-04-08 11:01:58 +02:00
2008-12-16 15:51:03 +01:00
context = * p_context ;
2009-04-03 16:43:40 +02:00
if ( likely ( ! context ) ) {
2008-12-16 15:51:03 +01:00
context = new_context ;
2008-04-08 11:01:58 +02:00
2008-12-16 15:51:03 +01:00
context - > this = p_context ;
context - > task = task ;
2009-04-03 16:43:40 +02:00
context - > cpu = cpu ;
2008-12-16 15:51:03 +01:00
context - > count = 0 ;
2008-04-08 11:01:58 +02:00
2008-12-16 15:51:03 +01:00
* p_context = context ;
}
2008-12-11 13:49:59 +01:00
2008-12-16 15:51:03 +01:00
context - > count + + ;
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:40 +02:00
spin_unlock_irq ( & ds_lock ) ;
2008-04-08 11:01:58 +02:00
2008-12-16 15:51:03 +01:00
if ( context ! = new_context )
kfree ( new_context ) ;
2008-04-08 11:01:58 +02:00
return context ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
static void ds_put_context ( struct ds_context * context )
2008-01-30 13:31:09 +01:00
{
2009-04-03 16:43:36 +02:00
struct task_struct * task ;
2008-11-25 08:52:56 +01:00
unsigned long irq ;
2008-04-08 11:01:58 +02:00
if ( ! context )
return ;
2008-11-25 08:52:56 +01:00
spin_lock_irqsave ( & ds_lock , irq ) ;
2008-04-08 11:01:58 +02:00
2008-12-11 13:49:59 +01:00
if ( - - context - > count ) {
spin_unlock_irqrestore ( & ds_lock , irq ) ;
return ;
}
2008-04-08 11:01:58 +02:00
2008-04-28 23:15:04 +04:00
* ( context - > this ) = NULL ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:36 +02:00
task = context - > task ;
if ( task )
clear_tsk_thread_flag ( task , TIF_DS_AREA_MSR ) ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
/*
* We leave the ( now dangling ) pointer to the DS configuration in
* the DS_AREA msr . This is as good or as bad as replacing it with
* NULL - the hardware would crash if we enabled tracing .
*
* This saves us some problems with having to write an msr on a
* different cpu while preventing others from doing the same for the
* next context for that same cpu .
*/
2008-04-08 11:01:58 +02:00
2008-11-25 08:52:56 +01:00
spin_unlock_irqrestore ( & ds_lock , irq ) ;
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:36 +02:00
/* The context might still be in use for context switching. */
if ( task & & ( task ! = current ) )
wait_task_context_switch ( task ) ;
2008-12-11 13:49:59 +01:00
kfree ( context ) ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
static void ds_install_ds_area ( struct ds_context * context )
{
unsigned long ds ;
ds = ( unsigned long ) context - > ds ;
/*
* There is a race between the bts master and the pebs master .
*
* The thread / cpu access is synchronized via get / put_cpu ( ) for
* task tracing and via wrmsr_on_cpu for cpu tracing .
*
* If bts and pebs are collected for the same task or same cpu ,
* the same confiuration is written twice .
*/
if ( context - > task ) {
get_cpu ( ) ;
if ( context - > task = = current )
wrmsrl ( MSR_IA32_DS_AREA , ds ) ;
set_tsk_thread_flag ( context - > task , TIF_DS_AREA_MSR ) ;
put_cpu ( ) ;
} else
wrmsr_on_cpu ( context - > cpu , MSR_IA32_DS_AREA ,
( u32 ) ( ( u64 ) ds ) , ( u32 ) ( ( u64 ) ds > > 32 ) ) ;
}
2008-04-08 11:01:58 +02:00
/*
2008-12-11 13:49:59 +01:00
* Call the tracer ' s callback on a buffer overflow .
2008-04-08 11:01:58 +02:00
*
* context : the ds context
* qual : the buffer type
*/
2008-11-25 09:01:25 +01:00
static void ds_overflow ( struct ds_context * context , enum ds_qualifier qual )
{
switch ( qual ) {
2008-12-11 13:49:59 +01:00
case ds_bts :
if ( context - > bts_master & &
context - > bts_master - > ovfl )
context - > bts_master - > ovfl ( context - > bts_master ) ;
break ;
case ds_pebs :
if ( context - > pebs_master & &
context - > pebs_master - > ovfl )
context - > pebs_master - > ovfl ( context - > pebs_master ) ;
2008-11-25 09:01:25 +01:00
break ;
}
2008-12-11 13:49:59 +01:00
}
/*
* Write raw data into the BTS or PEBS buffer .
*
* The remainder of any partially written record is zeroed out .
*
* context : the DS context
2009-03-13 11:54:40 +01:00
* qual : the buffer type
* record : the data to write
* size : the size of the data
2008-12-11 13:49:59 +01:00
*/
static int ds_write ( struct ds_context * context , enum ds_qualifier qual ,
const void * record , size_t size )
{
int bytes_written = 0 ;
if ( ! record )
return - EINVAL ;
while ( size ) {
unsigned long base , index , end , write_end , int_th ;
unsigned long write_size , adj_write_size ;
/*
2009-03-13 10:46:42 +01:00
* Write as much as possible without producing an
2008-12-11 13:49:59 +01:00
* overflow interrupt .
*
2009-03-13 10:46:42 +01:00
* Interrupt_threshold must either be
2008-12-11 13:49:59 +01:00
* - bigger than absolute_maximum or
* - point to a record between buffer_base and absolute_maximum
*
2009-03-13 10:46:42 +01:00
* Index points to a valid record .
2008-12-11 13:49:59 +01:00
*/
base = ds_get ( context - > ds , qual , ds_buffer_base ) ;
index = ds_get ( context - > ds , qual , ds_index ) ;
end = ds_get ( context - > ds , qual , ds_absolute_maximum ) ;
int_th = ds_get ( context - > ds , qual , ds_interrupt_threshold ) ;
write_end = min ( end , int_th ) ;
2009-03-13 10:46:42 +01:00
/*
* If we are already beyond the interrupt threshold ,
* we fill the entire buffer .
*/
2008-12-11 13:49:59 +01:00
if ( write_end < = index )
write_end = end ;
if ( write_end < = index )
break ;
write_size = min ( ( unsigned long ) size , write_end - index ) ;
memcpy ( ( void * ) index , record , write_size ) ;
record = ( const char * ) record + write_size ;
size - = write_size ;
bytes_written + = write_size ;
adj_write_size = write_size / ds_cfg . sizeof_rec [ qual ] ;
adj_write_size * = ds_cfg . sizeof_rec [ qual ] ;
2009-03-13 10:46:42 +01:00
/* Zero out trailing bytes. */
2008-12-11 13:49:59 +01:00
memset ( ( char * ) index + write_size , 0 ,
adj_write_size - write_size ) ;
index + = adj_write_size ;
if ( index > = end )
index = base ;
ds_set ( context - > ds , qual , ds_index , index ) ;
if ( index > = int_th )
ds_overflow ( context , qual ) ;
}
return bytes_written ;
}
/*
* Branch Trace Store ( BTS ) uses the following format . Different
* architectures vary in the size of those fields .
* - source linear address
* - destination linear address
* - flags
*
* Later architectures use 64 bit pointers throughout , whereas earlier
* architectures use 32 bit pointers in 32 bit mode .
*
2009-03-13 10:42:18 +01:00
* We compute the base address for the fields based on :
2008-12-11 13:49:59 +01:00
* - the field size stored in the DS configuration
* - the relative field position
*
* In order to store additional information in the BTS buffer , we use
* a special source address to indicate that the record requires
* special interpretation .
*
* Netburst indicated via a bit in the flags field whether the branch
* was predicted ; this is ignored .
*
* We use two levels of abstraction :
* - the raw data level defined here
* - an arch - independent level defined in ds . h
*/
enum bts_field {
bts_from ,
bts_to ,
bts_flags ,
2009-03-13 11:54:40 +01:00
bts_qual = bts_from ,
2009-04-03 16:43:38 +02:00
bts_clock = bts_to ,
2009-03-13 11:54:40 +01:00
bts_pid = bts_flags ,
2008-12-11 13:49:59 +01:00
2009-03-13 11:54:40 +01:00
bts_qual_mask = ( bts_qual_max - 1 ) ,
bts_escape = ( ( unsigned long ) - 1 & ~ bts_qual_mask )
2008-12-11 13:49:59 +01:00
} ;
2009-06-20 16:15:40 +05:30
static inline unsigned long bts_get ( const char * base , unsigned long field )
2008-12-11 13:49:59 +01:00
{
2009-03-13 10:42:18 +01:00
base + = ( ds_cfg . sizeof_ptr_field * field ) ;
2008-12-11 13:49:59 +01:00
return * ( unsigned long * ) base ;
}
2009-06-20 16:15:40 +05:30
static inline void bts_set ( char * base , unsigned long field , unsigned long val )
2008-12-11 13:49:59 +01:00
{
2009-06-20 16:15:40 +05:30
base + = ( ds_cfg . sizeof_ptr_field * field ) ;
2008-12-11 13:49:59 +01:00
( * ( unsigned long * ) base ) = val ;
}
/*
* The raw BTS data is architecture dependent .
*
* For higher - level users , we give an arch - independent view .
* - ds . h defines struct bts_struct
* - bts_read translates one raw bts record into a bts_struct
* - bts_write translates one bts_struct into the raw format and
* writes it into the top of the parameter tracer ' s buffer .
*
* return : bytes read / written on success ; - Eerrno , otherwise
*/
2009-03-13 11:54:40 +01:00
static int
bts_read ( struct bts_tracer * tracer , const void * at , struct bts_struct * out )
2008-12-11 13:49:59 +01:00
{
if ( ! tracer )
return - EINVAL ;
if ( at < tracer - > trace . ds . begin )
return - EINVAL ;
if ( tracer - > trace . ds . end < ( at + tracer - > trace . ds . size ) )
return - EINVAL ;
memset ( out , 0 , sizeof ( * out ) ) ;
if ( ( bts_get ( at , bts_qual ) & ~ bts_qual_mask ) = = bts_escape ) {
out - > qualifier = ( bts_get ( at , bts_qual ) & bts_qual_mask ) ;
2009-04-03 16:43:38 +02:00
out - > variant . event . clock = bts_get ( at , bts_clock ) ;
out - > variant . event . pid = bts_get ( at , bts_pid ) ;
2008-12-11 13:49:59 +01:00
} else {
out - > qualifier = bts_branch ;
out - > variant . lbr . from = bts_get ( at , bts_from ) ;
out - > variant . lbr . to = bts_get ( at , bts_to ) ;
2008-12-16 15:53:11 +01:00
if ( ! out - > variant . lbr . from & & ! out - > variant . lbr . to )
out - > qualifier = bts_invalid ;
2008-12-11 13:49:59 +01:00
}
return ds_cfg . sizeof_rec [ ds_bts ] ;
}
static int bts_write ( struct bts_tracer * tracer , const struct bts_struct * in )
{
unsigned char raw [ MAX_SIZEOF_BTS ] ;
if ( ! tracer )
return - EINVAL ;
if ( MAX_SIZEOF_BTS < ds_cfg . sizeof_rec [ ds_bts ] )
return - EOVERFLOW ;
switch ( in - > qualifier ) {
case bts_invalid :
bts_set ( raw , bts_from , 0 ) ;
bts_set ( raw , bts_to , 0 ) ;
bts_set ( raw , bts_flags , 0 ) ;
break ;
case bts_branch :
bts_set ( raw , bts_from , in - > variant . lbr . from ) ;
bts_set ( raw , bts_to , in - > variant . lbr . to ) ;
bts_set ( raw , bts_flags , 0 ) ;
break ;
case bts_task_arrives :
case bts_task_departs :
bts_set ( raw , bts_qual , ( bts_escape | in - > qualifier ) ) ;
2009-04-03 16:43:38 +02:00
bts_set ( raw , bts_clock , in - > variant . event . clock ) ;
bts_set ( raw , bts_pid , in - > variant . event . pid ) ;
2008-11-25 09:01:25 +01:00
break ;
2008-12-11 13:49:59 +01:00
default :
return - EINVAL ;
2008-11-25 09:01:25 +01:00
}
2008-12-11 13:49:59 +01:00
return ds_write ( tracer - > ds . context , ds_bts , raw ,
ds_cfg . sizeof_rec [ ds_bts ] ) ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2008-12-11 13:49:59 +01:00
static void ds_write_config ( struct ds_context * context ,
struct ds_trace * cfg , enum ds_qualifier qual )
{
unsigned char * ds = context - > ds ;
ds_set ( ds , qual , ds_buffer_base , ( unsigned long ) cfg - > begin ) ;
ds_set ( ds , qual , ds_index , ( unsigned long ) cfg - > top ) ;
ds_set ( ds , qual , ds_absolute_maximum , ( unsigned long ) cfg - > end ) ;
ds_set ( ds , qual , ds_interrupt_threshold , ( unsigned long ) cfg - > ith ) ;
}
static void ds_read_config ( struct ds_context * context ,
struct ds_trace * cfg , enum ds_qualifier qual )
2008-01-30 13:31:09 +01:00
{
2008-12-11 13:49:59 +01:00
unsigned char * ds = context - > ds ;
cfg - > begin = ( void * ) ds_get ( ds , qual , ds_buffer_base ) ;
cfg - > top = ( void * ) ds_get ( ds , qual , ds_index ) ;
cfg - > end = ( void * ) ds_get ( ds , qual , ds_absolute_maximum ) ;
cfg - > ith = ( void * ) ds_get ( ds , qual , ds_interrupt_threshold ) ;
}
static void ds_init_ds_trace ( struct ds_trace * trace , enum ds_qualifier qual ,
void * base , size_t size , size_t ith ,
unsigned int flags ) {
2008-04-08 11:01:58 +02:00
unsigned long buffer , adj ;
2008-11-25 09:01:25 +01:00
2009-03-13 10:46:42 +01:00
/*
* Adjust the buffer address and size to meet alignment
2008-11-25 09:01:25 +01:00
* constraints :
* - buffer is double - word aligned
* - size is multiple of record size
*
* We checked the size at the very beginning ; we have enough
* space to do the adjustment .
*/
buffer = ( unsigned long ) base ;
adj = ALIGN ( buffer , DS_ALIGNMENT ) - buffer ;
buffer + = adj ;
size - = adj ;
2008-12-11 13:49:59 +01:00
trace - > n = size / ds_cfg . sizeof_rec [ qual ] ;
trace - > size = ds_cfg . sizeof_rec [ qual ] ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
size = ( trace - > n * trace - > size ) ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
trace - > begin = ( void * ) buffer ;
trace - > top = trace - > begin ;
trace - > end = ( void * ) ( buffer + size ) ;
2009-03-13 10:46:42 +01:00
/*
* The value for ' no threshold ' is - 1 , which will set the
2008-11-25 09:01:25 +01:00
* threshold outside of the buffer , just like we want it .
*/
2009-04-03 16:43:40 +02:00
ith * = ds_cfg . sizeof_rec [ qual ] ;
2008-12-11 13:49:59 +01:00
trace - > ith = ( void * ) ( buffer + size - ith ) ;
trace - > flags = flags ;
2008-11-25 09:01:25 +01:00
}
2008-12-11 13:49:59 +01:00
static int ds_request ( struct ds_tracer * tracer , struct ds_trace * trace ,
enum ds_qualifier qual , struct task_struct * task ,
2009-04-03 16:43:40 +02:00
int cpu , void * base , size_t size , size_t th )
2008-11-25 09:01:25 +01:00
{
struct ds_context * context ;
int error ;
2009-04-03 16:43:51 +02:00
size_t req_size ;
2008-04-08 11:01:58 +02:00
2009-03-13 10:42:18 +01:00
error = - EOPNOTSUPP ;
if ( ! ds_cfg . sizeof_rec [ qual ] )
goto out ;
2008-11-25 09:05:27 +01:00
error = - EINVAL ;
if ( ! base )
goto out ;
2009-04-03 16:43:51 +02:00
req_size = ds_cfg . sizeof_rec [ qual ] ;
/* We might need space for alignment adjustments. */
if ( ! IS_ALIGNED ( ( unsigned long ) base , DS_ALIGNMENT ) )
req_size + = DS_ALIGNMENT ;
2008-11-25 09:01:25 +01:00
error = - EINVAL ;
2009-04-03 16:43:51 +02:00
if ( size < req_size )
2008-11-25 09:01:25 +01:00
goto out ;
2008-04-08 11:01:58 +02:00
2008-11-25 09:01:25 +01:00
if ( th ! = ( size_t ) - 1 ) {
th * = ds_cfg . sizeof_rec [ qual ] ;
error = - EINVAL ;
if ( size < = th )
goto out ;
}
tracer - > buffer = base ;
tracer - > size = size ;
2008-04-08 11:01:58 +02:00
2008-11-25 09:01:25 +01:00
error = - ENOMEM ;
2009-04-03 16:43:40 +02:00
context = ds_get_context ( task , cpu ) ;
2008-04-08 11:01:58 +02:00
if ( ! context )
2008-11-25 09:01:25 +01:00
goto out ;
tracer - > context = context ;
2009-04-03 16:43:40 +02:00
/*
* Defer any tracer - specific initialization work for the context until
* context ownership has been clarified .
*/
2008-11-25 08:52:56 +01:00
2008-12-11 13:49:59 +01:00
error = 0 ;
2008-11-25 09:01:25 +01:00
out :
2008-04-08 11:01:58 +02:00
return error ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
static struct bts_tracer * ds_request_bts ( struct task_struct * task , int cpu ,
void * base , size_t size ,
bts_ovfl_callback_t ovfl , size_t th ,
unsigned int flags )
2008-01-30 13:31:09 +01:00
{
2008-11-25 09:01:25 +01:00
struct bts_tracer * tracer ;
int error ;
2008-04-08 11:01:58 +02:00
2009-03-13 10:46:42 +01:00
/* Buffer overflow notification is not yet implemented. */
2008-11-25 09:01:25 +01:00
error = - EOPNOTSUPP ;
if ( ovfl )
goto out ;
2009-04-03 16:43:37 +02:00
error = get_tracer ( task ) ;
if ( error < 0 )
goto out ;
2008-11-25 09:01:25 +01:00
error = - ENOMEM ;
2009-04-03 16:43:40 +02:00
tracer = kzalloc ( sizeof ( * tracer ) , GFP_KERNEL ) ;
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2009-04-03 16:43:37 +02:00
goto out_put_tracer ;
2008-11-25 09:01:25 +01:00
tracer - > ovfl = ovfl ;
2009-04-03 16:43:40 +02:00
/* Do some more error checking and acquire a tracing context. */
2008-12-11 13:49:59 +01:00
error = ds_request ( & tracer - > ds , & tracer - > trace . ds ,
2009-04-03 16:43:40 +02:00
ds_bts , task , cpu , base , size , th ) ;
2008-11-25 09:01:25 +01:00
if ( error < 0 )
goto out_tracer ;
2009-04-03 16:43:40 +02:00
/* Claim the bts part of the tracing context we acquired above. */
spin_lock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
error = - EPERM ;
if ( tracer - > ds . context - > bts_master )
2009-04-03 16:43:37 +02:00
goto out_unlock ;
2008-12-11 13:49:59 +01:00
tracer - > ds . context - > bts_master = tracer ;
2009-04-03 16:43:40 +02:00
spin_unlock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:40 +02:00
/*
* Now that we own the bts part of the context , let ' s complete the
* initialization for that part .
*/
ds_init_ds_trace ( & tracer - > trace . ds , ds_bts , base , size , th , flags ) ;
ds_write_config ( tracer - > ds . context , & tracer - > trace . ds , ds_bts ) ;
ds_install_ds_area ( tracer - > ds . context ) ;
2008-12-11 13:49:59 +01:00
tracer - > trace . read = bts_read ;
tracer - > trace . write = bts_write ;
2009-04-03 16:43:40 +02:00
/* Start tracing. */
2008-12-11 13:49:59 +01:00
ds_resume_bts ( tracer ) ;
2008-11-25 09:01:25 +01:00
return tracer ;
2008-12-11 13:49:59 +01:00
out_unlock :
2009-04-03 16:43:40 +02:00
spin_unlock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
ds_put_context ( tracer - > ds . context ) ;
2008-11-25 09:01:25 +01:00
out_tracer :
2008-11-25 09:05:27 +01:00
kfree ( tracer ) ;
2009-04-03 16:43:37 +02:00
out_put_tracer :
put_tracer ( task ) ;
2008-11-25 09:01:25 +01:00
out :
return ERR_PTR ( error ) ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
struct bts_tracer * ds_request_bts_task ( struct task_struct * task ,
void * base , size_t size ,
bts_ovfl_callback_t ovfl ,
size_t th , unsigned int flags )
{
return ds_request_bts ( task , 0 , base , size , ovfl , th , flags ) ;
}
struct bts_tracer * ds_request_bts_cpu ( int cpu , void * base , size_t size ,
bts_ovfl_callback_t ovfl ,
size_t th , unsigned int flags )
{
return ds_request_bts ( NULL , cpu , base , size , ovfl , th , flags ) ;
}
static struct pebs_tracer * ds_request_pebs ( struct task_struct * task , int cpu ,
void * base , size_t size ,
pebs_ovfl_callback_t ovfl , size_t th ,
unsigned int flags )
2008-01-30 13:31:09 +01:00
{
2008-11-25 09:01:25 +01:00
struct pebs_tracer * tracer ;
2008-04-08 11:01:58 +02:00
int error ;
2009-03-13 10:46:42 +01:00
/* Buffer overflow notification is not yet implemented. */
2008-11-25 09:01:25 +01:00
error = - EOPNOTSUPP ;
if ( ovfl )
2008-04-08 11:01:58 +02:00
goto out ;
2009-04-03 16:43:37 +02:00
error = get_tracer ( task ) ;
if ( error < 0 )
goto out ;
2008-11-25 09:01:25 +01:00
error = - ENOMEM ;
2009-04-03 16:43:40 +02:00
tracer = kzalloc ( sizeof ( * tracer ) , GFP_KERNEL ) ;
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2009-04-03 16:43:37 +02:00
goto out_put_tracer ;
2008-11-25 09:01:25 +01:00
tracer - > ovfl = ovfl ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
/* Do some more error checking and acquire a tracing context. */
2008-12-11 13:49:59 +01:00
error = ds_request ( & tracer - > ds , & tracer - > trace . ds ,
2009-04-03 16:43:40 +02:00
ds_pebs , task , cpu , base , size , th ) ;
2008-11-25 09:01:25 +01:00
if ( error < 0 )
goto out_tracer ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
/* Claim the pebs part of the tracing context we acquired above. */
spin_lock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
error = - EPERM ;
if ( tracer - > ds . context - > pebs_master )
2009-04-03 16:43:37 +02:00
goto out_unlock ;
2008-12-11 13:49:59 +01:00
tracer - > ds . context - > pebs_master = tracer ;
2009-04-03 16:43:40 +02:00
spin_unlock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:40 +02:00
/*
* Now that we own the pebs part of the context , let ' s complete the
* initialization for that part .
*/
ds_init_ds_trace ( & tracer - > trace . ds , ds_pebs , base , size , th , flags ) ;
2009-03-05 08:57:21 +01:00
ds_write_config ( tracer - > ds . context , & tracer - > trace . ds , ds_pebs ) ;
2009-04-03 16:43:40 +02:00
ds_install_ds_area ( tracer - > ds . context ) ;
/* Start tracing. */
2008-12-11 13:49:59 +01:00
ds_resume_pebs ( tracer ) ;
2008-11-25 09:01:25 +01:00
return tracer ;
2008-12-11 13:49:59 +01:00
out_unlock :
2009-04-03 16:43:40 +02:00
spin_unlock_irq ( & ds_lock ) ;
2008-12-11 13:49:59 +01:00
ds_put_context ( tracer - > ds . context ) ;
2008-11-25 09:01:25 +01:00
out_tracer :
2008-11-25 09:05:27 +01:00
kfree ( tracer ) ;
2009-04-03 16:43:37 +02:00
out_put_tracer :
put_tracer ( task ) ;
2008-04-08 11:01:58 +02:00
out :
2008-11-25 09:01:25 +01:00
return ERR_PTR ( error ) ;
}
2009-04-03 16:43:40 +02:00
struct pebs_tracer * ds_request_pebs_task ( struct task_struct * task ,
void * base , size_t size ,
pebs_ovfl_callback_t ovfl ,
size_t th , unsigned int flags )
2008-01-30 13:31:09 +01:00
{
2009-04-03 16:43:40 +02:00
return ds_request_pebs ( task , 0 , base , size , ovfl , th , flags ) ;
}
2009-04-03 16:43:36 +02:00
2009-04-03 16:43:40 +02:00
struct pebs_tracer * ds_request_pebs_cpu ( int cpu , void * base , size_t size ,
pebs_ovfl_callback_t ovfl ,
size_t th , unsigned int flags )
{
return ds_request_pebs ( NULL , cpu , base , size , ovfl , th , flags ) ;
}
2008-11-25 09:01:25 +01:00
2009-04-03 16:43:40 +02:00
static void ds_free_bts ( struct bts_tracer * tracer )
{
struct task_struct * task ;
2009-04-03 16:43:36 +02:00
2009-04-03 16:43:40 +02:00
task = tracer - > ds . context - > task ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
WARN_ON_ONCE ( tracer - > ds . context - > bts_master ! = tracer ) ;
tracer - > ds . context - > bts_master = NULL ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:36 +02:00
/* Make sure tracing stopped and the tracer is not in use. */
if ( task & & ( task ! = current ) )
wait_task_context_switch ( task ) ;
2008-12-11 13:49:59 +01:00
ds_put_context ( tracer - > ds . context ) ;
2009-04-03 16:43:37 +02:00
put_tracer ( task ) ;
2008-11-25 09:01:25 +01:00
kfree ( tracer ) ;
2008-01-30 13:31:09 +01:00
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
void ds_release_bts ( struct bts_tracer * tracer )
{
might_sleep ( ) ;
if ( ! tracer )
return ;
ds_suspend_bts ( tracer ) ;
ds_free_bts ( tracer ) ;
}
int ds_release_bts_noirq ( struct bts_tracer * tracer )
{
struct task_struct * task ;
unsigned long irq ;
int error ;
if ( ! tracer )
return 0 ;
task = tracer - > ds . context - > task ;
local_irq_save ( irq ) ;
error = - EPERM ;
if ( ! task & &
( tracer - > ds . context - > cpu ! = smp_processor_id ( ) ) )
goto out ;
error = - EPERM ;
if ( task & & ( task ! = current ) )
goto out ;
ds_suspend_bts_noirq ( tracer ) ;
ds_free_bts ( tracer ) ;
error = 0 ;
out :
local_irq_restore ( irq ) ;
return error ;
}
static void update_task_debugctlmsr ( struct task_struct * task ,
unsigned long debugctlmsr )
{
task - > thread . debugctlmsr = debugctlmsr ;
get_cpu ( ) ;
if ( task = = current )
update_debugctlmsr ( debugctlmsr ) ;
put_cpu ( ) ;
}
2008-12-11 13:49:59 +01:00
void ds_suspend_bts ( struct bts_tracer * tracer )
2008-01-30 13:31:09 +01:00
{
2008-12-11 13:49:59 +01:00
struct task_struct * task ;
2009-04-03 16:43:40 +02:00
unsigned long debugctlmsr ;
int cpu ;
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2008-12-11 13:49:59 +01:00
return ;
2008-11-25 09:01:25 +01:00
2009-04-03 16:43:33 +02:00
tracer - > flags = 0 ;
2008-12-11 13:49:59 +01:00
task = tracer - > ds . context - > task ;
2009-04-03 16:43:40 +02:00
cpu = tracer - > ds . context - > cpu ;
2008-11-25 09:01:25 +01:00
2009-04-03 16:43:40 +02:00
WARN_ON ( ! task & & irqs_disabled ( ) ) ;
2008-11-25 09:01:25 +01:00
2009-04-03 16:43:40 +02:00
debugctlmsr = ( task ?
task - > thread . debugctlmsr :
get_debugctlmsr_on_cpu ( cpu ) ) ;
debugctlmsr & = ~ BTS_CONTROL ;
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:40 +02:00
if ( task )
update_task_debugctlmsr ( task , debugctlmsr ) ;
else
update_debugctlmsr_on_cpu ( cpu , debugctlmsr ) ;
2008-04-08 11:01:58 +02:00
}
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:40 +02:00
int ds_suspend_bts_noirq ( struct bts_tracer * tracer )
2008-04-08 11:01:58 +02:00
{
2008-12-11 13:49:59 +01:00
struct task_struct * task ;
2009-04-03 16:43:40 +02:00
unsigned long debugctlmsr , irq ;
int cpu , error = 0 ;
2008-01-30 13:31:09 +01:00
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2009-04-03 16:43:40 +02:00
return 0 ;
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:40 +02:00
tracer - > flags = 0 ;
2009-04-03 16:43:33 +02:00
2008-12-11 13:49:59 +01:00
task = tracer - > ds . context - > task ;
2009-04-03 16:43:40 +02:00
cpu = tracer - > ds . context - > cpu ;
local_irq_save ( irq ) ;
error = - EPERM ;
if ( ! task & & ( cpu ! = smp_processor_id ( ) ) )
goto out ;
debugctlmsr = ( task ?
task - > thread . debugctlmsr :
get_debugctlmsr ( ) ) ;
debugctlmsr & = ~ BTS_CONTROL ;
if ( task )
update_task_debugctlmsr ( task , debugctlmsr ) ;
else
update_debugctlmsr ( debugctlmsr ) ;
error = 0 ;
out :
local_irq_restore ( irq ) ;
return error ;
}
static unsigned long ds_bts_control ( struct bts_tracer * tracer )
{
unsigned long control ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
control = ds_cfg . ctl [ dsf_bts ] ;
if ( ! ( tracer - > trace . ds . flags & BTS_KERNEL ) )
control | = ds_cfg . ctl [ dsf_bts_kernel ] ;
if ( ! ( tracer - > trace . ds . flags & BTS_USER ) )
control | = ds_cfg . ctl [ dsf_bts_user ] ;
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:40 +02:00
return control ;
2008-01-30 13:31:09 +01:00
}
2009-04-03 16:43:40 +02:00
void ds_resume_bts ( struct bts_tracer * tracer )
2008-01-30 13:31:09 +01:00
{
2009-04-03 16:43:37 +02:00
struct task_struct * task ;
2009-04-03 16:43:40 +02:00
unsigned long debugctlmsr ;
int cpu ;
2009-04-03 16:43:37 +02:00
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2008-12-11 13:49:59 +01:00
return ;
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:40 +02:00
tracer - > flags = tracer - > trace . ds . flags ;
2009-04-03 16:43:37 +02:00
task = tracer - > ds . context - > task ;
2009-04-03 16:43:40 +02:00
cpu = tracer - > ds . context - > cpu ;
2009-04-03 16:43:37 +02:00
2009-04-03 16:43:40 +02:00
WARN_ON ( ! task & & irqs_disabled ( ) ) ;
debugctlmsr = ( task ?
task - > thread . debugctlmsr :
get_debugctlmsr_on_cpu ( cpu ) ) ;
debugctlmsr | = ds_bts_control ( tracer ) ;
if ( task )
update_task_debugctlmsr ( task , debugctlmsr ) ;
else
update_debugctlmsr_on_cpu ( cpu , debugctlmsr ) ;
}
int ds_resume_bts_noirq ( struct bts_tracer * tracer )
{
struct task_struct * task ;
unsigned long debugctlmsr , irq ;
int cpu , error = 0 ;
if ( ! tracer )
return 0 ;
tracer - > flags = tracer - > trace . ds . flags ;
task = tracer - > ds . context - > task ;
cpu = tracer - > ds . context - > cpu ;
local_irq_save ( irq ) ;
error = - EPERM ;
if ( ! task & & ( cpu ! = smp_processor_id ( ) ) )
goto out ;
debugctlmsr = ( task ?
task - > thread . debugctlmsr :
get_debugctlmsr ( ) ) ;
debugctlmsr | = ds_bts_control ( tracer ) ;
if ( task )
update_task_debugctlmsr ( task , debugctlmsr ) ;
else
update_debugctlmsr ( debugctlmsr ) ;
error = 0 ;
out :
local_irq_restore ( irq ) ;
return error ;
}
static void ds_free_pebs ( struct pebs_tracer * tracer )
{
struct task_struct * task ;
task = tracer - > ds . context - > task ;
2008-04-08 11:01:58 +02:00
2008-12-11 13:49:59 +01:00
WARN_ON_ONCE ( tracer - > ds . context - > pebs_master ! = tracer ) ;
tracer - > ds . context - > pebs_master = NULL ;
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
ds_put_context ( tracer - > ds . context ) ;
2009-04-03 16:43:37 +02:00
put_tracer ( task ) ;
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
kfree ( tracer ) ;
2008-01-30 13:31:20 +01:00
}
2009-04-03 16:43:40 +02:00
void ds_release_pebs ( struct pebs_tracer * tracer )
{
might_sleep ( ) ;
if ( ! tracer )
return ;
ds_suspend_pebs ( tracer ) ;
ds_free_pebs ( tracer ) ;
}
int ds_release_pebs_noirq ( struct pebs_tracer * tracer )
{
struct task_struct * task ;
unsigned long irq ;
int error ;
if ( ! tracer )
return 0 ;
task = tracer - > ds . context - > task ;
local_irq_save ( irq ) ;
error = - EPERM ;
if ( ! task & &
( tracer - > ds . context - > cpu ! = smp_processor_id ( ) ) )
goto out ;
error = - EPERM ;
if ( task & & ( task ! = current ) )
goto out ;
ds_suspend_pebs_noirq ( tracer ) ;
ds_free_pebs ( tracer ) ;
error = 0 ;
out :
local_irq_restore ( irq ) ;
return error ;
}
2008-12-11 13:49:59 +01:00
void ds_suspend_pebs ( struct pebs_tracer * tracer )
2008-01-30 13:31:20 +01:00
{
2008-04-08 11:01:58 +02:00
}
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:40 +02:00
int ds_suspend_pebs_noirq ( struct pebs_tracer * tracer )
{
return 0 ;
}
2008-12-11 13:49:59 +01:00
void ds_resume_pebs ( struct pebs_tracer * tracer )
2008-04-08 11:01:58 +02:00
{
2008-01-30 13:31:09 +01:00
}
2009-04-03 16:43:40 +02:00
int ds_resume_pebs_noirq ( struct pebs_tracer * tracer )
{
return 0 ;
}
2008-12-11 13:49:59 +01:00
const struct bts_trace * ds_read_bts ( struct bts_tracer * tracer )
2008-01-30 13:31:09 +01:00
{
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2008-12-11 13:49:59 +01:00
return NULL ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
ds_read_config ( tracer - > ds . context , & tracer - > trace . ds , ds_bts ) ;
return & tracer - > trace ;
2008-04-08 11:01:58 +02:00
}
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
const struct pebs_trace * ds_read_pebs ( struct pebs_tracer * tracer )
2008-04-08 11:01:58 +02:00
{
2008-11-25 09:01:25 +01:00
if ( ! tracer )
2008-12-11 13:49:59 +01:00
return NULL ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
ds_read_config ( tracer - > ds . context , & tracer - > trace . ds , ds_pebs ) ;
2009-04-03 16:43:52 +02:00
tracer - > trace . counters = ds_cfg . nr_counter_reset ;
memcpy ( tracer - > trace . counter_reset ,
tracer - > ds . context - > ds +
( NUM_DS_PTR_FIELDS * ds_cfg . sizeof_ptr_field ) ,
ds_cfg . nr_counter_reset * PEBS_RESET_FIELD_SIZE ) ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
return & tracer - > trace ;
2008-04-08 11:01:58 +02:00
}
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
int ds_reset_bts ( struct bts_tracer * tracer )
2008-04-08 11:01:58 +02:00
{
2008-11-25 09:01:25 +01:00
if ( ! tracer )
return - EINVAL ;
2008-12-11 13:49:59 +01:00
tracer - > trace . ds . top = tracer - > trace . ds . begin ;
2008-11-25 09:01:25 +01:00
2008-12-11 13:49:59 +01:00
ds_set ( tracer - > ds . context - > ds , ds_bts , ds_index ,
( unsigned long ) tracer - > trace . ds . top ) ;
2008-11-25 09:01:25 +01:00
return 0 ;
2008-04-08 11:01:58 +02:00
}
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
int ds_reset_pebs ( struct pebs_tracer * tracer )
2008-04-08 11:01:58 +02:00
{
2008-11-25 09:01:25 +01:00
if ( ! tracer )
return - EINVAL ;
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
tracer - > trace . ds . top = tracer - > trace . ds . begin ;
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:50 +02:00
ds_set ( tracer - > ds . context - > ds , ds_pebs , ds_index ,
2008-12-11 13:49:59 +01:00
( unsigned long ) tracer - > trace . ds . top ) ;
2008-04-08 11:01:58 +02:00
2008-11-25 09:01:25 +01:00
return 0 ;
2008-01-30 13:31:09 +01:00
}
2009-04-03 16:43:52 +02:00
int ds_set_pebs_reset ( struct pebs_tracer * tracer ,
unsigned int counter , u64 value )
2008-01-30 13:31:09 +01:00
{
2008-11-25 09:01:25 +01:00
if ( ! tracer )
return - EINVAL ;
2008-01-30 13:31:09 +01:00
2009-04-03 16:43:52 +02:00
if ( ds_cfg . nr_counter_reset < counter )
return - EINVAL ;
2009-03-13 10:42:18 +01:00
* ( u64 * ) ( tracer - > ds . context - > ds +
2009-04-03 16:43:52 +02:00
( NUM_DS_PTR_FIELDS * ds_cfg . sizeof_ptr_field ) +
( counter * PEBS_RESET_FIELD_SIZE ) ) = value ;
2008-04-08 11:01:58 +02:00
2008-11-25 09:01:25 +01:00
return 0 ;
2008-04-08 11:01:58 +02:00
}
2008-12-11 13:49:59 +01:00
static const struct ds_configuration ds_cfg_netburst = {
2009-01-19 10:38:35 +01:00
. name = " Netburst " ,
2008-12-11 13:49:59 +01:00
. ctl [ dsf_bts ] = ( 1 < < 2 ) | ( 1 < < 3 ) ,
. ctl [ dsf_bts_kernel ] = ( 1 < < 5 ) ,
. ctl [ dsf_bts_user ] = ( 1 < < 6 ) ,
2009-04-03 16:43:52 +02:00
. nr_counter_reset = 1 ,
2008-01-30 13:31:09 +01:00
} ;
2008-12-11 13:49:59 +01:00
static const struct ds_configuration ds_cfg_pentium_m = {
2009-01-19 10:38:35 +01:00
. name = " Pentium M " ,
2008-12-11 13:49:59 +01:00
. ctl [ dsf_bts ] = ( 1 < < 6 ) | ( 1 < < 7 ) ,
2009-04-03 16:43:52 +02:00
. nr_counter_reset = 1 ,
2008-01-30 13:31:09 +01:00
} ;
2009-01-19 10:38:35 +01:00
static const struct ds_configuration ds_cfg_core2_atom = {
. name = " Core 2/Atom " ,
2008-12-11 13:49:59 +01:00
. ctl [ dsf_bts ] = ( 1 < < 6 ) | ( 1 < < 7 ) ,
. ctl [ dsf_bts_kernel ] = ( 1 < < 9 ) ,
. ctl [ dsf_bts_user ] = ( 1 < < 10 ) ,
2009-04-03 16:43:52 +02:00
. nr_counter_reset = 1 ,
} ;
static const struct ds_configuration ds_cfg_core_i7 = {
. name = " Core i7 " ,
. ctl [ dsf_bts ] = ( 1 < < 6 ) | ( 1 < < 7 ) ,
. ctl [ dsf_bts_kernel ] = ( 1 < < 9 ) ,
. ctl [ dsf_bts_user ] = ( 1 < < 10 ) ,
. nr_counter_reset = 4 ,
2008-12-11 13:49:59 +01:00
} ;
2008-01-30 13:31:09 +01:00
2008-12-11 13:49:59 +01:00
static void
2009-03-13 10:42:18 +01:00
ds_configure ( const struct ds_configuration * cfg ,
struct cpuinfo_x86 * cpu )
2008-01-30 13:31:09 +01:00
{
2009-03-13 10:42:18 +01:00
unsigned long nr_pebs_fields = 0 ;
printk ( KERN_INFO " [ds] using %s configuration \n " , cfg - > name ) ;
# ifdef __i386__
nr_pebs_fields = 10 ;
# else
nr_pebs_fields = 18 ;
# endif
2009-04-03 16:43:52 +02:00
/*
* Starting with version 2 , architectural performance
* monitoring supports a format specifier .
*/
if ( ( cpuid_eax ( 0xa ) & 0xff ) > 1 ) {
unsigned long perf_capabilities , format ;
rdmsrl ( MSR_IA32_PERF_CAPABILITIES , perf_capabilities ) ;
format = ( perf_capabilities > > 8 ) & 0xf ;
switch ( format ) {
case 0 :
nr_pebs_fields = 18 ;
break ;
case 1 :
nr_pebs_fields = 22 ;
break ;
default :
printk ( KERN_INFO
" [ds] unknown PEBS format: %lu \n " , format ) ;
nr_pebs_fields = 0 ;
break ;
}
}
2008-12-11 13:49:59 +01:00
memset ( & ds_cfg , 0 , sizeof ( ds_cfg ) ) ;
2008-01-30 13:31:09 +01:00
ds_cfg = * cfg ;
2008-11-25 09:01:25 +01:00
2009-03-13 10:42:18 +01:00
ds_cfg . sizeof_ptr_field =
( cpu_has ( cpu , X86_FEATURE_DTES64 ) ? 8 : 4 ) ;
2008-12-11 13:49:59 +01:00
2009-03-13 10:42:18 +01:00
ds_cfg . sizeof_rec [ ds_bts ] = ds_cfg . sizeof_ptr_field * 3 ;
ds_cfg . sizeof_rec [ ds_pebs ] = ds_cfg . sizeof_ptr_field * nr_pebs_fields ;
if ( ! cpu_has ( cpu , X86_FEATURE_BTS ) ) {
ds_cfg . sizeof_rec [ ds_bts ] = 0 ;
2008-12-11 13:49:59 +01:00
printk ( KERN_INFO " [ds] bts not available \n " ) ;
}
2009-03-13 10:42:18 +01:00
if ( ! cpu_has ( cpu , X86_FEATURE_PEBS ) ) {
ds_cfg . sizeof_rec [ ds_pebs ] = 0 ;
2008-12-11 13:49:59 +01:00
printk ( KERN_INFO " [ds] pebs not available \n " ) ;
2009-03-13 10:42:18 +01:00
}
printk ( KERN_INFO " [ds] sizes: address: %u bit, " ,
8 * ds_cfg . sizeof_ptr_field ) ;
printk ( " bts/pebs record: %u/%u bytes \n " ,
ds_cfg . sizeof_rec [ ds_bts ] , ds_cfg . sizeof_rec [ ds_pebs ] ) ;
2008-11-25 09:01:25 +01:00
2009-04-03 16:43:52 +02:00
WARN_ON_ONCE ( MAX_PEBS_COUNTERS < ds_cfg . nr_counter_reset ) ;
2008-01-30 13:31:09 +01:00
}
void __cpuinit ds_init_intel ( struct cpuinfo_x86 * c )
{
2009-04-03 16:43:47 +02:00
/* Only configure the first cpu. Others are identical. */
if ( ds_cfg . name )
return ;
2008-01-30 13:31:09 +01:00
switch ( c - > x86 ) {
case 0x6 :
switch ( c - > x86_model ) {
2009-01-19 10:38:35 +01:00
case 0x9 :
case 0xd : /* Pentium M */
2009-03-13 10:42:18 +01:00
ds_configure ( & ds_cfg_pentium_m , c ) ;
2008-01-30 13:31:09 +01:00
break ;
2009-01-19 10:38:35 +01:00
case 0xf :
case 0x17 : /* Core2 */
case 0x1c : /* Atom */
2009-03-13 10:42:18 +01:00
ds_configure ( & ds_cfg_core2_atom , c ) ;
2009-01-19 10:38:35 +01:00
break ;
2009-03-13 10:46:42 +01:00
case 0x1a : /* Core i7 */
2009-04-03 16:43:52 +02:00
ds_configure ( & ds_cfg_core_i7 , c ) ;
break ;
2009-01-19 10:38:35 +01:00
default :
2009-03-13 10:46:42 +01:00
/* Sorry, don't know about them. */
2008-01-30 13:31:09 +01:00
break ;
}
break ;
2009-01-19 10:38:35 +01:00
case 0xf :
2008-01-30 13:31:09 +01:00
switch ( c - > x86_model ) {
case 0x0 :
case 0x1 :
case 0x2 : /* Netburst */
2009-03-13 10:42:18 +01:00
ds_configure ( & ds_cfg_netburst , c ) ;
2008-01-30 13:31:09 +01:00
break ;
default :
2009-03-13 10:46:42 +01:00
/* Sorry, don't know about them. */
2008-01-30 13:31:09 +01:00
break ;
}
break ;
default :
2009-03-13 10:46:42 +01:00
/* Sorry, don't know about them. */
2008-01-30 13:31:09 +01:00
break ;
}
}
2008-04-08 11:01:58 +02:00
2009-04-03 16:43:33 +02:00
static inline void ds_take_timestamp ( struct ds_context * context ,
enum bts_qualifier qualifier ,
struct task_struct * task )
{
struct bts_tracer * tracer = context - > bts_master ;
struct bts_struct ts ;
/* Prevent compilers from reading the tracer pointer twice. */
barrier ( ) ;
if ( ! tracer | | ! ( tracer - > flags & BTS_TIMESTAMPS ) )
return ;
memset ( & ts , 0 , sizeof ( ts ) ) ;
2009-04-03 16:43:38 +02:00
ts . qualifier = qualifier ;
ts . variant . event . clock = trace_clock_global ( ) ;
ts . variant . event . pid = task - > pid ;
2009-04-03 16:43:33 +02:00
bts_write ( tracer , & ts ) ;
}
2008-12-11 13:49:59 +01:00
/*
* Change the DS configuration from tracing prev to tracing next .
*/
void ds_switch_to ( struct task_struct * prev , struct task_struct * next )
2008-04-08 11:01:58 +02:00
{
2009-04-03 16:43:33 +02:00
struct ds_context * prev_ctx = prev - > thread . ds_ctx ;
struct ds_context * next_ctx = next - > thread . ds_ctx ;
unsigned long debugctlmsr = next - > thread . debugctlmsr ;
/* Make sure all data is read before we start. */
barrier ( ) ;
2008-12-11 13:49:59 +01:00
if ( prev_ctx ) {
update_debugctlmsr ( 0 ) ;
2009-04-03 16:43:33 +02:00
ds_take_timestamp ( prev_ctx , bts_task_departs , prev ) ;
2008-12-11 13:49:59 +01:00
}
if ( next_ctx ) {
2009-04-03 16:43:33 +02:00
ds_take_timestamp ( next_ctx , bts_task_arrives , next ) ;
2008-12-11 13:49:59 +01:00
wrmsrl ( MSR_IA32_DS_AREA , ( unsigned long ) next_ctx - > ds ) ;
2008-11-25 09:01:25 +01:00
}
2008-12-11 13:49:59 +01:00
2009-04-03 16:43:33 +02:00
update_debugctlmsr ( debugctlmsr ) ;
2008-04-08 11:01:58 +02:00
}
2008-12-19 15:10:24 +01:00
2009-04-03 16:43:40 +02:00
static __init int ds_selftest ( void )
{
if ( ds_cfg . sizeof_rec [ ds_bts ] ) {
int error ;
error = ds_selftest_bts ( ) ;
if ( error ) {
WARN ( 1 , " [ds] selftest failed. disabling bts. \n " ) ;
ds_cfg . sizeof_rec [ ds_bts ] = 0 ;
}
}
if ( ds_cfg . sizeof_rec [ ds_pebs ] ) {
int error ;
error = ds_selftest_pebs ( ) ;
if ( error ) {
WARN ( 1 , " [ds] selftest failed. disabling pebs. \n " ) ;
ds_cfg . sizeof_rec [ ds_pebs ] = 0 ;
}
}
return 0 ;
}
device_initcall ( ds_selftest ) ;