1d94d71cbSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
273e75b41SHollis Blanchard /*
373e75b41SHollis Blanchard *
47b701591SHollis Blanchard * Copyright IBM Corp. 2008
573e75b41SHollis Blanchard *
673e75b41SHollis Blanchard * Authors: Hollis Blanchard <hollisb@us.ibm.com>
773e75b41SHollis Blanchard * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
873e75b41SHollis Blanchard */
973e75b41SHollis Blanchard
1073e75b41SHollis Blanchard #include <linux/kvm_host.h>
1173e75b41SHollis Blanchard #include <linux/fs.h>
1273e75b41SHollis Blanchard #include <linux/seq_file.h>
1373e75b41SHollis Blanchard #include <linux/debugfs.h>
1473e75b41SHollis Blanchard #include <linux/uaccess.h>
15e0ea8b2cSBenjamin Herrenschmidt #include <linux/module.h>
1673e75b41SHollis Blanchard
1773e75b41SHollis Blanchard #include <asm/time.h>
1873e75b41SHollis Blanchard #include <asm-generic/div64.h>
1973e75b41SHollis Blanchard
207b701591SHollis Blanchard #include "timing.h"
217b701591SHollis Blanchard
kvmppc_init_timing_stats(struct kvm_vcpu * vcpu)2273e75b41SHollis Blanchard void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu)
2373e75b41SHollis Blanchard {
2473e75b41SHollis Blanchard int i;
2573e75b41SHollis Blanchard
2609000adbSBharat Bhushan /* Take a lock to avoid concurrent updates */
2709000adbSBharat Bhushan mutex_lock(&vcpu->arch.exit_timing_lock);
2873e75b41SHollis Blanchard
2973e75b41SHollis Blanchard vcpu->arch.last_exit_type = 0xDEAD;
3073e75b41SHollis Blanchard for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
3173e75b41SHollis Blanchard vcpu->arch.timing_count_type[i] = 0;
3273e75b41SHollis Blanchard vcpu->arch.timing_max_duration[i] = 0;
3373e75b41SHollis Blanchard vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF;
3473e75b41SHollis Blanchard vcpu->arch.timing_sum_duration[i] = 0;
3573e75b41SHollis Blanchard vcpu->arch.timing_sum_quad_duration[i] = 0;
3673e75b41SHollis Blanchard }
3773e75b41SHollis Blanchard vcpu->arch.timing_last_exit = 0;
3873e75b41SHollis Blanchard vcpu->arch.timing_exit.tv64 = 0;
3973e75b41SHollis Blanchard vcpu->arch.timing_last_enter.tv64 = 0;
4073e75b41SHollis Blanchard
4109000adbSBharat Bhushan mutex_unlock(&vcpu->arch.exit_timing_lock);
4273e75b41SHollis Blanchard }
4373e75b41SHollis Blanchard
add_exit_timing(struct kvm_vcpu * vcpu,u64 duration,int type)447b701591SHollis Blanchard static void add_exit_timing(struct kvm_vcpu *vcpu, u64 duration, int type)
4573e75b41SHollis Blanchard {
4673e75b41SHollis Blanchard u64 old;
4773e75b41SHollis Blanchard
4809000adbSBharat Bhushan mutex_lock(&vcpu->arch.exit_timing_lock);
4909000adbSBharat Bhushan
5073e75b41SHollis Blanchard vcpu->arch.timing_count_type[type]++;
5173e75b41SHollis Blanchard
5273e75b41SHollis Blanchard /* sum */
5373e75b41SHollis Blanchard old = vcpu->arch.timing_sum_duration[type];
5473e75b41SHollis Blanchard vcpu->arch.timing_sum_duration[type] += duration;
5573e75b41SHollis Blanchard if (unlikely(old > vcpu->arch.timing_sum_duration[type])) {
5673e75b41SHollis Blanchard printk(KERN_ERR"%s - wrap adding sum of durations"
5773e75b41SHollis Blanchard " old %lld new %lld type %d exit # of type %d\n",
5873e75b41SHollis Blanchard __func__, old, vcpu->arch.timing_sum_duration[type],
5973e75b41SHollis Blanchard type, vcpu->arch.timing_count_type[type]);
6073e75b41SHollis Blanchard }
6173e75b41SHollis Blanchard
6273e75b41SHollis Blanchard /* square sum */
6373e75b41SHollis Blanchard old = vcpu->arch.timing_sum_quad_duration[type];
6473e75b41SHollis Blanchard vcpu->arch.timing_sum_quad_duration[type] += (duration*duration);
6573e75b41SHollis Blanchard if (unlikely(old > vcpu->arch.timing_sum_quad_duration[type])) {
6673e75b41SHollis Blanchard printk(KERN_ERR"%s - wrap adding sum of squared durations"
6773e75b41SHollis Blanchard " old %lld new %lld type %d exit # of type %d\n",
6873e75b41SHollis Blanchard __func__, old,
6973e75b41SHollis Blanchard vcpu->arch.timing_sum_quad_duration[type],
7073e75b41SHollis Blanchard type, vcpu->arch.timing_count_type[type]);
7173e75b41SHollis Blanchard }
7273e75b41SHollis Blanchard
7373e75b41SHollis Blanchard /* set min/max */
7473e75b41SHollis Blanchard if (unlikely(duration < vcpu->arch.timing_min_duration[type]))
7573e75b41SHollis Blanchard vcpu->arch.timing_min_duration[type] = duration;
7673e75b41SHollis Blanchard if (unlikely(duration > vcpu->arch.timing_max_duration[type]))
7773e75b41SHollis Blanchard vcpu->arch.timing_max_duration[type] = duration;
7809000adbSBharat Bhushan
7909000adbSBharat Bhushan mutex_unlock(&vcpu->arch.exit_timing_lock);
8073e75b41SHollis Blanchard }
8173e75b41SHollis Blanchard
kvmppc_update_timing_stats(struct kvm_vcpu * vcpu)8273e75b41SHollis Blanchard void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu)
8373e75b41SHollis Blanchard {
8473e75b41SHollis Blanchard u64 exit = vcpu->arch.timing_last_exit;
8573e75b41SHollis Blanchard u64 enter = vcpu->arch.timing_last_enter.tv64;
8673e75b41SHollis Blanchard
8773e75b41SHollis Blanchard /* save exit time, used next exit when the reenter time is known */
8873e75b41SHollis Blanchard vcpu->arch.timing_last_exit = vcpu->arch.timing_exit.tv64;
8973e75b41SHollis Blanchard
9073e75b41SHollis Blanchard if (unlikely(vcpu->arch.last_exit_type == 0xDEAD || exit == 0))
9173e75b41SHollis Blanchard return; /* skip incomplete cycle (e.g. after reset) */
9273e75b41SHollis Blanchard
9373e75b41SHollis Blanchard /* update statistics for average and standard deviation */
9473e75b41SHollis Blanchard add_exit_timing(vcpu, (enter - exit), vcpu->arch.last_exit_type);
9573e75b41SHollis Blanchard /* enter -> timing_last_exit is time spent in guest - log this too */
9673e75b41SHollis Blanchard add_exit_timing(vcpu, (vcpu->arch.timing_last_exit - enter),
9773e75b41SHollis Blanchard TIMEINGUEST);
9873e75b41SHollis Blanchard }
9973e75b41SHollis Blanchard
10073e75b41SHollis Blanchard static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = {
10173e75b41SHollis Blanchard [MMIO_EXITS] = "MMIO",
10273e75b41SHollis Blanchard [SIGNAL_EXITS] = "SIGNAL",
10373e75b41SHollis Blanchard [ITLB_REAL_MISS_EXITS] = "ITLBREAL",
10473e75b41SHollis Blanchard [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT",
10573e75b41SHollis Blanchard [DTLB_REAL_MISS_EXITS] = "DTLBREAL",
10673e75b41SHollis Blanchard [DTLB_VIRT_MISS_EXITS] = "DTLBVIRT",
10773e75b41SHollis Blanchard [SYSCALL_EXITS] = "SYSCALL",
10873e75b41SHollis Blanchard [ISI_EXITS] = "ISI",
10973e75b41SHollis Blanchard [DSI_EXITS] = "DSI",
11073e75b41SHollis Blanchard [EMULATED_INST_EXITS] = "EMULINST",
11173e75b41SHollis Blanchard [EMULATED_MTMSRWE_EXITS] = "EMUL_WAIT",
11273e75b41SHollis Blanchard [EMULATED_WRTEE_EXITS] = "EMUL_WRTEE",
11373e75b41SHollis Blanchard [EMULATED_MTSPR_EXITS] = "EMUL_MTSPR",
11473e75b41SHollis Blanchard [EMULATED_MFSPR_EXITS] = "EMUL_MFSPR",
11573e75b41SHollis Blanchard [EMULATED_MTMSR_EXITS] = "EMUL_MTMSR",
11673e75b41SHollis Blanchard [EMULATED_MFMSR_EXITS] = "EMUL_MFMSR",
11773e75b41SHollis Blanchard [EMULATED_TLBSX_EXITS] = "EMUL_TLBSX",
11873e75b41SHollis Blanchard [EMULATED_TLBWE_EXITS] = "EMUL_TLBWE",
11973e75b41SHollis Blanchard [EMULATED_RFI_EXITS] = "EMUL_RFI",
12073e75b41SHollis Blanchard [DEC_EXITS] = "DEC",
12173e75b41SHollis Blanchard [EXT_INTR_EXITS] = "EXTINT",
12273e75b41SHollis Blanchard [HALT_WAKEUP] = "HALT",
12373e75b41SHollis Blanchard [USR_PR_INST] = "USR_PR_INST",
12473e75b41SHollis Blanchard [FP_UNAVAIL] = "FP_UNAVAIL",
12573e75b41SHollis Blanchard [DEBUG_EXITS] = "DEBUG",
12673e75b41SHollis Blanchard [TIMEINGUEST] = "TIMEINGUEST"
12773e75b41SHollis Blanchard };
12873e75b41SHollis Blanchard
kvmppc_exit_timing_show(struct seq_file * m,void * private)12973e75b41SHollis Blanchard static int kvmppc_exit_timing_show(struct seq_file *m, void *private)
13073e75b41SHollis Blanchard {
13173e75b41SHollis Blanchard struct kvm_vcpu *vcpu = m->private;
13273e75b41SHollis Blanchard int i;
1331a040b26SStuart Yoder u64 min, max, sum, sum_quad;
1347b701591SHollis Blanchard
13516273010SMarkus Elfring seq_puts(m, "type count min max sum sum_squared\n");
1361a040b26SStuart Yoder
13773e75b41SHollis Blanchard for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
1381a040b26SStuart Yoder
1391a040b26SStuart Yoder min = vcpu->arch.timing_min_duration[i];
1401a040b26SStuart Yoder do_div(min, tb_ticks_per_usec);
1411a040b26SStuart Yoder max = vcpu->arch.timing_max_duration[i];
1421a040b26SStuart Yoder do_div(max, tb_ticks_per_usec);
1431a040b26SStuart Yoder sum = vcpu->arch.timing_sum_duration[i];
1441a040b26SStuart Yoder do_div(sum, tb_ticks_per_usec);
1451a040b26SStuart Yoder sum_quad = vcpu->arch.timing_sum_quad_duration[i];
1461a040b26SStuart Yoder do_div(sum_quad, tb_ticks_per_usec);
1471a040b26SStuart Yoder
1487b701591SHollis Blanchard seq_printf(m, "%12s %10d %10lld %10lld %20lld %20lld\n",
1497b701591SHollis Blanchard kvm_exit_names[i],
1507b701591SHollis Blanchard vcpu->arch.timing_count_type[i],
1511a040b26SStuart Yoder min,
1521a040b26SStuart Yoder max,
1531a040b26SStuart Yoder sum,
1541a040b26SStuart Yoder sum_quad);
1551a040b26SStuart Yoder
15673e75b41SHollis Blanchard }
15773e75b41SHollis Blanchard return 0;
15873e75b41SHollis Blanchard }
15973e75b41SHollis Blanchard
1607b701591SHollis Blanchard /* Write 'c' to clear the timing statistics. */
kvmppc_exit_timing_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)16173e75b41SHollis Blanchard static ssize_t kvmppc_exit_timing_write(struct file *file,
16273e75b41SHollis Blanchard const char __user *user_buf,
16373e75b41SHollis Blanchard size_t count, loff_t *ppos)
16473e75b41SHollis Blanchard {
1657b701591SHollis Blanchard int err = -EINVAL;
16673e75b41SHollis Blanchard char c;
16773e75b41SHollis Blanchard
1687b701591SHollis Blanchard if (count > 1) {
16973e75b41SHollis Blanchard goto done;
17073e75b41SHollis Blanchard }
17173e75b41SHollis Blanchard
1727b701591SHollis Blanchard if (get_user(c, user_buf)) {
17373e75b41SHollis Blanchard err = -EFAULT;
17473e75b41SHollis Blanchard goto done;
17573e75b41SHollis Blanchard }
17673e75b41SHollis Blanchard
17773e75b41SHollis Blanchard if (c == 'c') {
178ea01c6b4SJoe Perches struct seq_file *seqf = file->private_data;
17973e75b41SHollis Blanchard struct kvm_vcpu *vcpu = seqf->private;
1807b701591SHollis Blanchard /* Write does not affect our buffers previously generated with
1817b701591SHollis Blanchard * show. seq_file is locked here to prevent races of init with
18273e75b41SHollis Blanchard * a show call */
18373e75b41SHollis Blanchard mutex_lock(&seqf->lock);
18473e75b41SHollis Blanchard kvmppc_init_timing_stats(vcpu);
18573e75b41SHollis Blanchard mutex_unlock(&seqf->lock);
18673e75b41SHollis Blanchard err = count;
18773e75b41SHollis Blanchard }
18873e75b41SHollis Blanchard
18973e75b41SHollis Blanchard done:
19073e75b41SHollis Blanchard return err;
19173e75b41SHollis Blanchard }
19273e75b41SHollis Blanchard
kvmppc_exit_timing_open(struct inode * inode,struct file * file)19373e75b41SHollis Blanchard static int kvmppc_exit_timing_open(struct inode *inode, struct file *file)
19473e75b41SHollis Blanchard {
19573e75b41SHollis Blanchard return single_open(file, kvmppc_exit_timing_show, inode->i_private);
19673e75b41SHollis Blanchard }
19773e75b41SHollis Blanchard
198828c0950SAlexey Dobriyan static const struct file_operations kvmppc_exit_timing_fops = {
19973e75b41SHollis Blanchard .owner = THIS_MODULE,
20073e75b41SHollis Blanchard .open = kvmppc_exit_timing_open,
20173e75b41SHollis Blanchard .read = seq_read,
20273e75b41SHollis Blanchard .write = kvmppc_exit_timing_write,
20373e75b41SHollis Blanchard .llseek = seq_lseek,
20473e75b41SHollis Blanchard .release = single_release,
20573e75b41SHollis Blanchard };
20673e75b41SHollis Blanchard
kvmppc_create_vcpu_debugfs_e500(struct kvm_vcpu * vcpu,struct dentry * debugfs_dentry)207*faf01aefSAlexey Kardashevskiy int kvmppc_create_vcpu_debugfs_e500(struct kvm_vcpu *vcpu,
208*faf01aefSAlexey Kardashevskiy struct dentry *debugfs_dentry)
20973e75b41SHollis Blanchard {
210*faf01aefSAlexey Kardashevskiy debugfs_create_file("timing", 0666, debugfs_dentry,
211c4fd527fSGreg Kroah-Hartman vcpu, &kvmppc_exit_timing_fops);
212*faf01aefSAlexey Kardashevskiy return 0;
21373e75b41SHollis Blanchard }
214