2019-05-29 17:12:40 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-12-03 00:51:57 +03:00
/*
*
2008-12-03 00:51:58 +03:00
* Copyright IBM Corp . 2008
2008-12-03 00:51:57 +03:00
*
* Authors : Hollis Blanchard < hollisb @ us . ibm . com >
* Christian Ehrhardt < ehrhardt @ linux . vnet . ibm . com >
*/
# include <linux/kvm_host.h>
# include <linux/fs.h>
# include <linux/seq_file.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
2009-11-05 09:17:12 +03:00
# include <linux/module.h>
2008-12-03 00:51:57 +03:00
# include <asm/time.h>
# include <asm-generic/div64.h>
2008-12-03 00:51:58 +03:00
# include "timing.h"
2008-12-03 00:51:57 +03:00
void kvmppc_init_timing_stats ( struct kvm_vcpu * vcpu )
{
int i ;
2011-03-25 08:02:13 +03:00
/* Take a lock to avoid concurrent updates */
mutex_lock ( & vcpu - > arch . exit_timing_lock ) ;
2008-12-03 00:51:57 +03:00
vcpu - > arch . last_exit_type = 0xDEAD ;
for ( i = 0 ; i < __NUMBER_OF_KVM_EXIT_TYPES ; i + + ) {
vcpu - > arch . timing_count_type [ i ] = 0 ;
vcpu - > arch . timing_max_duration [ i ] = 0 ;
vcpu - > arch . timing_min_duration [ i ] = 0xFFFFFFFF ;
vcpu - > arch . timing_sum_duration [ i ] = 0 ;
vcpu - > arch . timing_sum_quad_duration [ i ] = 0 ;
}
vcpu - > arch . timing_last_exit = 0 ;
vcpu - > arch . timing_exit . tv64 = 0 ;
vcpu - > arch . timing_last_enter . tv64 = 0 ;
2011-03-25 08:02:13 +03:00
mutex_unlock ( & vcpu - > arch . exit_timing_lock ) ;
2008-12-03 00:51:57 +03:00
}
2008-12-03 00:51:58 +03:00
static void add_exit_timing ( struct kvm_vcpu * vcpu , u64 duration , int type )
2008-12-03 00:51:57 +03:00
{
u64 old ;
2011-03-25 08:02:13 +03:00
mutex_lock ( & vcpu - > arch . exit_timing_lock ) ;
2008-12-03 00:51:57 +03:00
vcpu - > arch . timing_count_type [ type ] + + ;
/* sum */
old = vcpu - > arch . timing_sum_duration [ type ] ;
vcpu - > arch . timing_sum_duration [ type ] + = duration ;
if ( unlikely ( old > vcpu - > arch . timing_sum_duration [ type ] ) ) {
printk ( KERN_ERR " %s - wrap adding sum of durations "
" old %lld new %lld type %d exit # of type %d \n " ,
__func__ , old , vcpu - > arch . timing_sum_duration [ type ] ,
type , vcpu - > arch . timing_count_type [ type ] ) ;
}
/* square sum */
old = vcpu - > arch . timing_sum_quad_duration [ type ] ;
vcpu - > arch . timing_sum_quad_duration [ type ] + = ( duration * duration ) ;
if ( unlikely ( old > vcpu - > arch . timing_sum_quad_duration [ type ] ) ) {
printk ( KERN_ERR " %s - wrap adding sum of squared durations "
" old %lld new %lld type %d exit # of type %d \n " ,
__func__ , old ,
vcpu - > arch . timing_sum_quad_duration [ type ] ,
type , vcpu - > arch . timing_count_type [ type ] ) ;
}
/* set min/max */
if ( unlikely ( duration < vcpu - > arch . timing_min_duration [ type ] ) )
vcpu - > arch . timing_min_duration [ type ] = duration ;
if ( unlikely ( duration > vcpu - > arch . timing_max_duration [ type ] ) )
vcpu - > arch . timing_max_duration [ type ] = duration ;
2011-03-25 08:02:13 +03:00
mutex_unlock ( & vcpu - > arch . exit_timing_lock ) ;
2008-12-03 00:51:57 +03:00
}
void kvmppc_update_timing_stats ( struct kvm_vcpu * vcpu )
{
u64 exit = vcpu - > arch . timing_last_exit ;
u64 enter = vcpu - > arch . timing_last_enter . tv64 ;
/* save exit time, used next exit when the reenter time is known */
vcpu - > arch . timing_last_exit = vcpu - > arch . timing_exit . tv64 ;
if ( unlikely ( vcpu - > arch . last_exit_type = = 0xDEAD | | exit = = 0 ) )
return ; /* skip incomplete cycle (e.g. after reset) */
/* update statistics for average and standard deviation */
add_exit_timing ( vcpu , ( enter - exit ) , vcpu - > arch . last_exit_type ) ;
/* enter -> timing_last_exit is time spent in guest - log this too */
add_exit_timing ( vcpu , ( vcpu - > arch . timing_last_exit - enter ) ,
TIMEINGUEST ) ;
}
static const char * kvm_exit_names [ __NUMBER_OF_KVM_EXIT_TYPES ] = {
2008-12-03 00:51:58 +03:00
[ MMIO_EXITS ] = " MMIO " ,
[ SIGNAL_EXITS ] = " SIGNAL " ,
[ ITLB_REAL_MISS_EXITS ] = " ITLBREAL " ,
[ ITLB_VIRT_MISS_EXITS ] = " ITLBVIRT " ,
[ DTLB_REAL_MISS_EXITS ] = " DTLBREAL " ,
[ DTLB_VIRT_MISS_EXITS ] = " DTLBVIRT " ,
[ SYSCALL_EXITS ] = " SYSCALL " ,
[ ISI_EXITS ] = " ISI " ,
[ DSI_EXITS ] = " DSI " ,
[ EMULATED_INST_EXITS ] = " EMULINST " ,
[ EMULATED_MTMSRWE_EXITS ] = " EMUL_WAIT " ,
[ EMULATED_WRTEE_EXITS ] = " EMUL_WRTEE " ,
[ EMULATED_MTSPR_EXITS ] = " EMUL_MTSPR " ,
[ EMULATED_MFSPR_EXITS ] = " EMUL_MFSPR " ,
[ EMULATED_MTMSR_EXITS ] = " EMUL_MTMSR " ,
[ EMULATED_MFMSR_EXITS ] = " EMUL_MFMSR " ,
[ EMULATED_TLBSX_EXITS ] = " EMUL_TLBSX " ,
[ EMULATED_TLBWE_EXITS ] = " EMUL_TLBWE " ,
[ EMULATED_RFI_EXITS ] = " EMUL_RFI " ,
[ DEC_EXITS ] = " DEC " ,
[ EXT_INTR_EXITS ] = " EXTINT " ,
[ HALT_WAKEUP ] = " HALT " ,
[ USR_PR_INST ] = " USR_PR_INST " ,
[ FP_UNAVAIL ] = " FP_UNAVAIL " ,
[ DEBUG_EXITS ] = " DEBUG " ,
[ TIMEINGUEST ] = " TIMEINGUEST "
2008-12-03 00:51:57 +03:00
} ;
static int kvmppc_exit_timing_show ( struct seq_file * m , void * private )
{
struct kvm_vcpu * vcpu = m - > private ;
int i ;
2011-03-29 00:01:56 +04:00
u64 min , max , sum , sum_quad ;
2008-12-03 00:51:58 +03:00
2018-01-07 12:07:36 +03:00
seq_puts ( m , " type count min max sum sum_squared \n " ) ;
2011-03-29 00:01:56 +04:00
2008-12-03 00:51:57 +03:00
for ( i = 0 ; i < __NUMBER_OF_KVM_EXIT_TYPES ; i + + ) {
2011-03-29 00:01:56 +04:00
min = vcpu - > arch . timing_min_duration [ i ] ;
do_div ( min , tb_ticks_per_usec ) ;
max = vcpu - > arch . timing_max_duration [ i ] ;
do_div ( max , tb_ticks_per_usec ) ;
sum = vcpu - > arch . timing_sum_duration [ i ] ;
do_div ( sum , tb_ticks_per_usec ) ;
sum_quad = vcpu - > arch . timing_sum_quad_duration [ i ] ;
do_div ( sum_quad , tb_ticks_per_usec ) ;
2008-12-03 00:51:58 +03:00
seq_printf ( m , " %12s %10d %10lld %10lld %20lld %20lld \n " ,
kvm_exit_names [ i ] ,
vcpu - > arch . timing_count_type [ i ] ,
2011-03-29 00:01:56 +04:00
min ,
max ,
sum ,
sum_quad ) ;
2008-12-03 00:51:57 +03:00
}
return 0 ;
}
2008-12-03 00:51:58 +03:00
/* Write 'c' to clear the timing statistics. */
2008-12-03 00:51:57 +03:00
static ssize_t kvmppc_exit_timing_write ( struct file * file ,
const char __user * user_buf ,
size_t count , loff_t * ppos )
{
2008-12-03 00:51:58 +03:00
int err = - EINVAL ;
2008-12-03 00:51:57 +03:00
char c ;
2008-12-03 00:51:58 +03:00
if ( count > 1 ) {
2008-12-03 00:51:57 +03:00
goto done ;
}
2008-12-03 00:51:58 +03:00
if ( get_user ( c , user_buf ) ) {
2008-12-03 00:51:57 +03:00
err = - EFAULT ;
goto done ;
}
if ( c = = ' c ' ) {
2010-07-12 14:49:55 +04:00
struct seq_file * seqf = file - > private_data ;
2008-12-03 00:51:57 +03:00
struct kvm_vcpu * vcpu = seqf - > private ;
2008-12-03 00:51:58 +03:00
/* Write does not affect our buffers previously generated with
* show . seq_file is locked here to prevent races of init with
2008-12-03 00:51:57 +03:00
* a show call */
mutex_lock ( & seqf - > lock ) ;
kvmppc_init_timing_stats ( vcpu ) ;
mutex_unlock ( & seqf - > lock ) ;
err = count ;
}
done :
return err ;
}
static int kvmppc_exit_timing_open ( struct inode * inode , struct file * file )
{
return single_open ( file , kvmppc_exit_timing_show , inode - > i_private ) ;
}
2009-10-02 02:43:56 +04:00
static const struct file_operations kvmppc_exit_timing_fops = {
2008-12-03 00:51:57 +03:00
. owner = THIS_MODULE ,
. open = kvmppc_exit_timing_open ,
. read = seq_read ,
. write = kvmppc_exit_timing_write ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2022-01-11 03:54:04 +03:00
int kvmppc_create_vcpu_debugfs_e500 ( struct kvm_vcpu * vcpu ,
struct dentry * debugfs_dentry )
2008-12-03 00:51:57 +03:00
{
2022-01-11 03:54:04 +03:00
debugfs_create_file ( " timing " , 0666 , debugfs_dentry ,
vcpu , & kvmppc_exit_timing_fops ) ;
return 0 ;
2008-12-03 00:51:57 +03:00
}