2017-11-24 17:00:32 +03:00
// SPDX-License-Identifier: GPL-2.0
2012-03-23 14:13:06 +04:00
/*
* Performance event support for s390x
*
2013-12-12 20:54:57 +04:00
* Copyright IBM Corp . 2012 , 2013
2012-03-23 14:13:06 +04:00
* Author ( s ) : Hendrik Brueckner < brueckner @ linux . vnet . ibm . com >
*/
# define KMSG_COMPONENT "perf"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
# include <linux/kernel.h>
# include <linux/perf_event.h>
2013-06-12 15:54:56 +04:00
# include <linux/kvm_host.h>
2012-03-23 14:13:06 +04:00
# include <linux/percpu.h>
# include <linux/export.h>
2013-12-13 15:38:39 +04:00
# include <linux/seq_file.h>
2013-12-12 19:32:47 +04:00
# include <linux/spinlock.h>
2013-12-11 15:44:40 +04:00
# include <linux/sysfs.h>
2012-03-23 14:13:06 +04:00
# include <asm/irq.h>
# include <asm/cpu_mf.h>
# include <asm/lowcore.h>
# include <asm/processor.h>
2013-12-13 15:38:39 +04:00
# include <asm/sysinfo.h>
2019-01-28 10:33:08 +03:00
# include <asm/unwind.h>
2012-03-23 14:13:06 +04:00
const char * perf_pmu_name ( void )
{
if ( cpum_cf_avail ( ) | | cpum_sf_avail ( ) )
2013-12-13 15:38:39 +04:00
return " CPU-Measurement Facilities (CPU-MF) " ;
2012-03-23 14:13:06 +04:00
return " pmu " ;
}
EXPORT_SYMBOL ( perf_pmu_name ) ;
int perf_num_counters ( void )
{
int num = 0 ;
if ( cpum_cf_avail ( ) )
num + = PERF_CPUM_CF_MAX_CTR ;
2013-12-12 19:32:47 +04:00
if ( cpum_sf_avail ( ) )
num + = PERF_CPUM_SF_MAX_CTR ;
2012-03-23 14:13:06 +04:00
return num ;
}
EXPORT_SYMBOL ( perf_num_counters ) ;
2013-06-12 15:54:56 +04:00
static struct kvm_s390_sie_block * sie_block ( struct pt_regs * regs )
{
struct stack_frame * stack = ( struct stack_frame * ) regs - > gprs [ 15 ] ;
if ( ! stack )
return NULL ;
return ( struct kvm_s390_sie_block * ) stack - > empty1 [ 0 ] ;
}
static bool is_in_guest ( struct pt_regs * regs )
{
if ( user_mode ( regs ) )
return false ;
2013-11-01 13:08:20 +04:00
# if IS_ENABLED(CONFIG_KVM)
2013-07-25 13:16:48 +04:00
return instruction_pointer ( regs ) = = ( unsigned long ) & sie_exit ;
# else
return false ;
# endif
2013-06-12 15:54:56 +04:00
}
static unsigned long guest_is_user_mode ( struct pt_regs * regs )
{
return sie_block ( regs ) - > gpsw . mask & PSW_MASK_PSTATE ;
}
static unsigned long instruction_pointer_guest ( struct pt_regs * regs )
{
2016-01-18 15:12:19 +03:00
return sie_block ( regs ) - > gpsw . addr ;
2013-06-12 15:54:56 +04:00
}
unsigned long perf_instruction_pointer ( struct pt_regs * regs )
{
return is_in_guest ( regs ) ? instruction_pointer_guest ( regs )
: instruction_pointer ( regs ) ;
}
static unsigned long perf_misc_guest_flags ( struct pt_regs * regs )
{
return guest_is_user_mode ( regs ) ? PERF_RECORD_MISC_GUEST_USER
: PERF_RECORD_MISC_GUEST_KERNEL ;
}
2013-12-12 20:54:57 +04:00
static unsigned long perf_misc_flags_sf ( struct pt_regs * regs )
{
struct perf_sf_sde_regs * sde_regs ;
unsigned long flags ;
sde_regs = ( struct perf_sf_sde_regs * ) & regs - > int_parm_long ;
if ( sde_regs - > in_guest )
flags = user_mode ( regs ) ? PERF_RECORD_MISC_GUEST_USER
: PERF_RECORD_MISC_GUEST_KERNEL ;
else
flags = user_mode ( regs ) ? PERF_RECORD_MISC_USER
: PERF_RECORD_MISC_KERNEL ;
return flags ;
}
2013-06-12 15:54:56 +04:00
unsigned long perf_misc_flags ( struct pt_regs * regs )
{
2013-12-12 20:54:57 +04:00
/* Check if the cpum_sf PMU has created the pt_regs structure.
* In this case , perf misc flags can be easily extracted . Otherwise ,
* do regular checks on the pt_regs content .
*/
if ( regs - > int_code = = 0x1407 & & regs - > int_parm = = CPU_MF_INT_SF_PRA )
if ( ! regs - > gprs [ 15 ] )
return perf_misc_flags_sf ( regs ) ;
2013-06-12 15:54:56 +04:00
if ( is_in_guest ( regs ) )
return perf_misc_guest_flags ( regs ) ;
return user_mode ( regs ) ? PERF_RECORD_MISC_USER
: PERF_RECORD_MISC_KERNEL ;
}
2014-03-11 13:49:20 +04:00
static void print_debug_cf ( void )
2012-03-23 14:13:06 +04:00
{
struct cpumf_ctr_info cf_info ;
2013-12-12 19:32:47 +04:00
int cpu = smp_processor_id ( ) ;
2012-03-23 14:13:06 +04:00
memset ( & cf_info , 0 , sizeof ( cf_info ) ) ;
2013-09-02 11:48:48 +04:00
if ( ! qctri ( & cf_info ) )
2012-03-23 14:13:06 +04:00
pr_info ( " CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x \n " ,
cpu , cf_info . cfvn , cf_info . csvn ,
cf_info . auth_ctl , cf_info . enable_ctl , cf_info . act_ctl ) ;
2013-12-12 19:32:47 +04:00
}
2012-03-23 14:13:06 +04:00
2013-12-12 19:32:47 +04:00
static void print_debug_sf ( void )
{
struct hws_qsi_info_block si ;
int cpu = smp_processor_id ( ) ;
memset ( & si , 0 , sizeof ( si ) ) ;
2013-12-13 14:42:44 +04:00
if ( qsi ( & si ) )
2013-12-12 19:32:47 +04:00
return ;
2014-01-08 19:45:39 +04:00
pr_info ( " CPU[%i] CPUM_SF: basic=%i diag=%i min=%lu max=%lu cpu_speed=%u \n " ,
2013-12-13 14:42:44 +04:00
cpu , si . as , si . ad , si . min_sampl_rate , si . max_sampl_rate ,
si . cpu_speed ) ;
if ( si . as )
pr_info ( " CPU[%i] CPUM_SF: Basic-sampling: a=%i e=%i c=%i "
2014-01-08 19:45:39 +04:00
" bsdes=%i tear=%016lx dear=%016lx \n " , cpu ,
2013-12-13 14:42:44 +04:00
si . as , si . es , si . cs , si . bsdes , si . tear , si . dear ) ;
if ( si . ad )
pr_info ( " CPU[%i] CPUM_SF: Diagnostic-sampling: a=%i e=%i c=%i "
2014-01-08 19:45:39 +04:00
" dsdes=%i tear=%016lx dear=%016lx \n " , cpu ,
2013-12-13 14:42:44 +04:00
si . ad , si . ed , si . cd , si . dsdes , si . tear , si . dear ) ;
2013-12-12 19:32:47 +04:00
}
void perf_event_print_debug ( void )
{
unsigned long flags ;
local_irq_save ( flags ) ;
if ( cpum_cf_avail ( ) )
print_debug_cf ( ) ;
if ( cpum_sf_avail ( ) )
print_debug_sf ( ) ;
2012-03-23 14:13:06 +04:00
local_irq_restore ( flags ) ;
}
2013-12-13 15:38:39 +04:00
/* Service level infrastructure */
static void sl_print_counter ( struct seq_file * m )
{
struct cpumf_ctr_info ci ;
memset ( & ci , 0 , sizeof ( ci ) ) ;
if ( qctri ( & ci ) )
return ;
seq_printf ( m , " CPU-MF: Counter facility: version=%u.%u "
" authorization=%04x \n " , ci . cfvn , ci . csvn , ci . auth_ctl ) ;
}
static void sl_print_sampling ( struct seq_file * m )
{
struct hws_qsi_info_block si ;
memset ( & si , 0 , sizeof ( si ) ) ;
if ( qsi ( & si ) )
return ;
if ( ! si . as & & ! si . ad )
return ;
seq_printf ( m , " CPU-MF: Sampling facility: min_rate=%lu max_rate=%lu "
" cpu_speed=%u \n " , si . min_sampl_rate , si . max_sampl_rate ,
si . cpu_speed ) ;
if ( si . as )
seq_printf ( m , " CPU-MF: Sampling facility: mode=basic "
" sample_size=%u \n " , si . bsdes ) ;
if ( si . ad )
seq_printf ( m , " CPU-MF: Sampling facility: mode=diagnostic "
" sample_size=%u \n " , si . dsdes ) ;
}
static void service_level_perf_print ( struct seq_file * m ,
struct service_level * sl )
{
if ( cpum_cf_avail ( ) )
sl_print_counter ( m ) ;
if ( cpum_sf_avail ( ) )
sl_print_sampling ( m ) ;
}
static struct service_level service_level_perf = {
. seq_print = service_level_perf_print ,
} ;
static int __init service_level_perf_register ( void )
{
return register_service_level ( & service_level_perf ) ;
}
arch_initcall ( service_level_perf_register ) ;
2016-04-28 18:30:53 +03:00
void perf_callchain_kernel ( struct perf_callchain_entry_ctx * entry ,
2012-03-23 14:13:06 +04:00
struct pt_regs * regs )
{
2019-01-28 10:33:08 +03:00
struct unwind_state state ;
2019-10-30 16:20:32 +03:00
unsigned long addr ;
2019-01-28 10:33:08 +03:00
2019-10-30 16:20:32 +03:00
unwind_for_each_frame ( & state , current , regs , 0 ) {
addr = unwind_get_return_address ( & state ) ;
if ( ! addr | | perf_callchain_store ( entry , addr ) )
return ;
}
2012-03-23 14:13:06 +04:00
}
2013-12-11 15:44:40 +04:00
2016-03-04 22:20:04 +03:00
/* Perf definitions for PMU event attributes in sysfs */
2013-12-11 15:44:40 +04:00
ssize_t cpumf_events_sysfs_show ( struct device * dev ,
struct device_attribute * attr , char * page )
{
struct perf_pmu_events_attr * pmu_attr ;
pmu_attr = container_of ( attr , struct perf_pmu_events_attr , attr ) ;
2017-05-23 11:49:35 +03:00
return sprintf ( page , " event=0x%04llx \n " , pmu_attr - > id ) ;
2013-12-11 15:44:40 +04:00
}