1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright IBM Corp. 2008
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Authors: Hollis Blanchard <hollisb@us.ibm.com>
7*4882a593Smuzhiyun * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/kvm_host.h>
11*4882a593Smuzhiyun #include <linux/fs.h>
12*4882a593Smuzhiyun #include <linux/seq_file.h>
13*4882a593Smuzhiyun #include <linux/debugfs.h>
14*4882a593Smuzhiyun #include <linux/uaccess.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <asm/time.h>
18*4882a593Smuzhiyun #include <asm-generic/div64.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "timing.h"
21*4882a593Smuzhiyun
kvmppc_init_timing_stats(struct kvm_vcpu * vcpu)22*4882a593Smuzhiyun void kvmppc_init_timing_stats(struct kvm_vcpu *vcpu)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun int i;
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun /* Take a lock to avoid concurrent updates */
27*4882a593Smuzhiyun mutex_lock(&vcpu->arch.exit_timing_lock);
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun vcpu->arch.last_exit_type = 0xDEAD;
30*4882a593Smuzhiyun for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
31*4882a593Smuzhiyun vcpu->arch.timing_count_type[i] = 0;
32*4882a593Smuzhiyun vcpu->arch.timing_max_duration[i] = 0;
33*4882a593Smuzhiyun vcpu->arch.timing_min_duration[i] = 0xFFFFFFFF;
34*4882a593Smuzhiyun vcpu->arch.timing_sum_duration[i] = 0;
35*4882a593Smuzhiyun vcpu->arch.timing_sum_quad_duration[i] = 0;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun vcpu->arch.timing_last_exit = 0;
38*4882a593Smuzhiyun vcpu->arch.timing_exit.tv64 = 0;
39*4882a593Smuzhiyun vcpu->arch.timing_last_enter.tv64 = 0;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun mutex_unlock(&vcpu->arch.exit_timing_lock);
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
add_exit_timing(struct kvm_vcpu * vcpu,u64 duration,int type)44*4882a593Smuzhiyun static void add_exit_timing(struct kvm_vcpu *vcpu, u64 duration, int type)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun u64 old;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun mutex_lock(&vcpu->arch.exit_timing_lock);
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun vcpu->arch.timing_count_type[type]++;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun /* sum */
53*4882a593Smuzhiyun old = vcpu->arch.timing_sum_duration[type];
54*4882a593Smuzhiyun vcpu->arch.timing_sum_duration[type] += duration;
55*4882a593Smuzhiyun if (unlikely(old > vcpu->arch.timing_sum_duration[type])) {
56*4882a593Smuzhiyun printk(KERN_ERR"%s - wrap adding sum of durations"
57*4882a593Smuzhiyun " old %lld new %lld type %d exit # of type %d\n",
58*4882a593Smuzhiyun __func__, old, vcpu->arch.timing_sum_duration[type],
59*4882a593Smuzhiyun type, vcpu->arch.timing_count_type[type]);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* square sum */
63*4882a593Smuzhiyun old = vcpu->arch.timing_sum_quad_duration[type];
64*4882a593Smuzhiyun vcpu->arch.timing_sum_quad_duration[type] += (duration*duration);
65*4882a593Smuzhiyun if (unlikely(old > vcpu->arch.timing_sum_quad_duration[type])) {
66*4882a593Smuzhiyun printk(KERN_ERR"%s - wrap adding sum of squared durations"
67*4882a593Smuzhiyun " old %lld new %lld type %d exit # of type %d\n",
68*4882a593Smuzhiyun __func__, old,
69*4882a593Smuzhiyun vcpu->arch.timing_sum_quad_duration[type],
70*4882a593Smuzhiyun type, vcpu->arch.timing_count_type[type]);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /* set min/max */
74*4882a593Smuzhiyun if (unlikely(duration < vcpu->arch.timing_min_duration[type]))
75*4882a593Smuzhiyun vcpu->arch.timing_min_duration[type] = duration;
76*4882a593Smuzhiyun if (unlikely(duration > vcpu->arch.timing_max_duration[type]))
77*4882a593Smuzhiyun vcpu->arch.timing_max_duration[type] = duration;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun mutex_unlock(&vcpu->arch.exit_timing_lock);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
kvmppc_update_timing_stats(struct kvm_vcpu * vcpu)82*4882a593Smuzhiyun void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun u64 exit = vcpu->arch.timing_last_exit;
85*4882a593Smuzhiyun u64 enter = vcpu->arch.timing_last_enter.tv64;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* save exit time, used next exit when the reenter time is known */
88*4882a593Smuzhiyun vcpu->arch.timing_last_exit = vcpu->arch.timing_exit.tv64;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (unlikely(vcpu->arch.last_exit_type == 0xDEAD || exit == 0))
91*4882a593Smuzhiyun return; /* skip incomplete cycle (e.g. after reset) */
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun /* update statistics for average and standard deviation */
94*4882a593Smuzhiyun add_exit_timing(vcpu, (enter - exit), vcpu->arch.last_exit_type);
95*4882a593Smuzhiyun /* enter -> timing_last_exit is time spent in guest - log this too */
96*4882a593Smuzhiyun add_exit_timing(vcpu, (vcpu->arch.timing_last_exit - enter),
97*4882a593Smuzhiyun TIMEINGUEST);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = {
101*4882a593Smuzhiyun [MMIO_EXITS] = "MMIO",
102*4882a593Smuzhiyun [SIGNAL_EXITS] = "SIGNAL",
103*4882a593Smuzhiyun [ITLB_REAL_MISS_EXITS] = "ITLBREAL",
104*4882a593Smuzhiyun [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT",
105*4882a593Smuzhiyun [DTLB_REAL_MISS_EXITS] = "DTLBREAL",
106*4882a593Smuzhiyun [DTLB_VIRT_MISS_EXITS] = "DTLBVIRT",
107*4882a593Smuzhiyun [SYSCALL_EXITS] = "SYSCALL",
108*4882a593Smuzhiyun [ISI_EXITS] = "ISI",
109*4882a593Smuzhiyun [DSI_EXITS] = "DSI",
110*4882a593Smuzhiyun [EMULATED_INST_EXITS] = "EMULINST",
111*4882a593Smuzhiyun [EMULATED_MTMSRWE_EXITS] = "EMUL_WAIT",
112*4882a593Smuzhiyun [EMULATED_WRTEE_EXITS] = "EMUL_WRTEE",
113*4882a593Smuzhiyun [EMULATED_MTSPR_EXITS] = "EMUL_MTSPR",
114*4882a593Smuzhiyun [EMULATED_MFSPR_EXITS] = "EMUL_MFSPR",
115*4882a593Smuzhiyun [EMULATED_MTMSR_EXITS] = "EMUL_MTMSR",
116*4882a593Smuzhiyun [EMULATED_MFMSR_EXITS] = "EMUL_MFMSR",
117*4882a593Smuzhiyun [EMULATED_TLBSX_EXITS] = "EMUL_TLBSX",
118*4882a593Smuzhiyun [EMULATED_TLBWE_EXITS] = "EMUL_TLBWE",
119*4882a593Smuzhiyun [EMULATED_RFI_EXITS] = "EMUL_RFI",
120*4882a593Smuzhiyun [DEC_EXITS] = "DEC",
121*4882a593Smuzhiyun [EXT_INTR_EXITS] = "EXTINT",
122*4882a593Smuzhiyun [HALT_WAKEUP] = "HALT",
123*4882a593Smuzhiyun [USR_PR_INST] = "USR_PR_INST",
124*4882a593Smuzhiyun [FP_UNAVAIL] = "FP_UNAVAIL",
125*4882a593Smuzhiyun [DEBUG_EXITS] = "DEBUG",
126*4882a593Smuzhiyun [TIMEINGUEST] = "TIMEINGUEST"
127*4882a593Smuzhiyun };
128*4882a593Smuzhiyun
kvmppc_exit_timing_show(struct seq_file * m,void * private)129*4882a593Smuzhiyun static int kvmppc_exit_timing_show(struct seq_file *m, void *private)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun struct kvm_vcpu *vcpu = m->private;
132*4882a593Smuzhiyun int i;
133*4882a593Smuzhiyun u64 min, max, sum, sum_quad;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun seq_puts(m, "type count min max sum sum_squared\n");
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun for (i = 0; i < __NUMBER_OF_KVM_EXIT_TYPES; i++) {
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun min = vcpu->arch.timing_min_duration[i];
140*4882a593Smuzhiyun do_div(min, tb_ticks_per_usec);
141*4882a593Smuzhiyun max = vcpu->arch.timing_max_duration[i];
142*4882a593Smuzhiyun do_div(max, tb_ticks_per_usec);
143*4882a593Smuzhiyun sum = vcpu->arch.timing_sum_duration[i];
144*4882a593Smuzhiyun do_div(sum, tb_ticks_per_usec);
145*4882a593Smuzhiyun sum_quad = vcpu->arch.timing_sum_quad_duration[i];
146*4882a593Smuzhiyun do_div(sum_quad, tb_ticks_per_usec);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun seq_printf(m, "%12s %10d %10lld %10lld %20lld %20lld\n",
149*4882a593Smuzhiyun kvm_exit_names[i],
150*4882a593Smuzhiyun vcpu->arch.timing_count_type[i],
151*4882a593Smuzhiyun min,
152*4882a593Smuzhiyun max,
153*4882a593Smuzhiyun sum,
154*4882a593Smuzhiyun sum_quad);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun /* 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)161*4882a593Smuzhiyun static ssize_t kvmppc_exit_timing_write(struct file *file,
162*4882a593Smuzhiyun const char __user *user_buf,
163*4882a593Smuzhiyun size_t count, loff_t *ppos)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun int err = -EINVAL;
166*4882a593Smuzhiyun char c;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (count > 1) {
169*4882a593Smuzhiyun goto done;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun if (get_user(c, user_buf)) {
173*4882a593Smuzhiyun err = -EFAULT;
174*4882a593Smuzhiyun goto done;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (c == 'c') {
178*4882a593Smuzhiyun struct seq_file *seqf = file->private_data;
179*4882a593Smuzhiyun struct kvm_vcpu *vcpu = seqf->private;
180*4882a593Smuzhiyun /* Write does not affect our buffers previously generated with
181*4882a593Smuzhiyun * show. seq_file is locked here to prevent races of init with
182*4882a593Smuzhiyun * a show call */
183*4882a593Smuzhiyun mutex_lock(&seqf->lock);
184*4882a593Smuzhiyun kvmppc_init_timing_stats(vcpu);
185*4882a593Smuzhiyun mutex_unlock(&seqf->lock);
186*4882a593Smuzhiyun err = count;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun done:
190*4882a593Smuzhiyun return err;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
kvmppc_exit_timing_open(struct inode * inode,struct file * file)193*4882a593Smuzhiyun static int kvmppc_exit_timing_open(struct inode *inode, struct file *file)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun return single_open(file, kvmppc_exit_timing_show, inode->i_private);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun static const struct file_operations kvmppc_exit_timing_fops = {
199*4882a593Smuzhiyun .owner = THIS_MODULE,
200*4882a593Smuzhiyun .open = kvmppc_exit_timing_open,
201*4882a593Smuzhiyun .read = seq_read,
202*4882a593Smuzhiyun .write = kvmppc_exit_timing_write,
203*4882a593Smuzhiyun .llseek = seq_lseek,
204*4882a593Smuzhiyun .release = single_release,
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
kvmppc_create_vcpu_debugfs(struct kvm_vcpu * vcpu,unsigned int id)207*4882a593Smuzhiyun void kvmppc_create_vcpu_debugfs(struct kvm_vcpu *vcpu, unsigned int id)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun static char dbg_fname[50];
210*4882a593Smuzhiyun struct dentry *debugfs_file;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun snprintf(dbg_fname, sizeof(dbg_fname), "vm%u_vcpu%u_timing",
213*4882a593Smuzhiyun current->pid, id);
214*4882a593Smuzhiyun debugfs_file = debugfs_create_file(dbg_fname, 0666, kvm_debugfs_dir,
215*4882a593Smuzhiyun vcpu, &kvmppc_exit_timing_fops);
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun vcpu->arch.debugfs_exit_timing = debugfs_file;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
kvmppc_remove_vcpu_debugfs(struct kvm_vcpu * vcpu)220*4882a593Smuzhiyun void kvmppc_remove_vcpu_debugfs(struct kvm_vcpu *vcpu)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun debugfs_remove(vcpu->arch.debugfs_exit_timing);
223*4882a593Smuzhiyun vcpu->arch.debugfs_exit_timing = NULL;
224*4882a593Smuzhiyun }
225