xref: /OK3568_Linux_fs/kernel/arch/powerpc/kvm/timing.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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