xref: /linux/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <string.h>
4 #include "../kvm-stat.h"
5 #include "../evsel.h"
6 #include "../env.h"
7 #include "../../../arch/x86/include/uapi/asm/svm.h"
8 #include "../../../arch/x86/include/uapi/asm/vmx.h"
9 #include "../../../arch/x86/include/uapi/asm/kvm.h"
10 
11 define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
12 define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
13 
14 static const struct kvm_events_ops exit_events = {
15 	.is_begin_event = exit_event_begin,
16 	.is_end_event = exit_event_end,
17 	.decode_key = exit_event_decode_key,
18 	.name = "VM-EXIT"
19 };
20 
21 /*
22  * For the mmio events, we treat:
23  * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
24  * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
25  */
26 static void mmio_event_get_key(struct perf_sample *sample,
27 			       struct event_key *key)
28 {
29 	key->key  = perf_sample__intval(sample, "gpa");
30 	key->info = perf_sample__intval(sample, "type");
31 }
32 
33 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0
34 #define KVM_TRACE_MMIO_READ 1
35 #define KVM_TRACE_MMIO_WRITE 2
36 
37 static bool mmio_event_begin(struct perf_sample *sample, struct event_key *key)
38 {
39 	/* MMIO read begin event in kernel. */
40 	if (kvm_exit_event(sample->evsel))
41 		return true;
42 
43 	/* MMIO write begin event in kernel. */
44 	if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") &&
45 	    perf_sample__intval(sample, "type") == KVM_TRACE_MMIO_WRITE) {
46 		mmio_event_get_key(sample, key);
47 		return true;
48 	}
49 
50 	return false;
51 }
52 
53 static bool mmio_event_end(struct perf_sample *sample, struct event_key *key)
54 {
55 	/* MMIO write end event in kernel. */
56 	if (kvm_entry_event(sample->evsel))
57 		return true;
58 
59 	/* MMIO read end event in kernel.*/
60 	if (evsel__name_is(sample->evsel, "kvm:kvm_mmio") &&
61 	    perf_sample__intval(sample, "type") == KVM_TRACE_MMIO_READ) {
62 		mmio_event_get_key(sample, key);
63 		return true;
64 	}
65 
66 	return false;
67 }
68 
69 static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
70 				  struct event_key *key,
71 				  char *decode)
72 {
73 	scnprintf(decode, KVM_EVENT_NAME_LEN, "%#lx:%s",
74 		  (unsigned long)key->key,
75 		  key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
76 }
77 
78 static const struct kvm_events_ops mmio_events = {
79 	.is_begin_event = mmio_event_begin,
80 	.is_end_event = mmio_event_end,
81 	.decode_key = mmio_event_decode_key,
82 	.name = "MMIO Access"
83 };
84 
85  /* The time of emulation pio access is from kvm_pio to kvm_entry. */
86 static void ioport_event_get_key(struct perf_sample *sample,
87 				 struct event_key *key)
88 {
89 	key->key  = perf_sample__intval(sample, "port");
90 	key->info = perf_sample__intval(sample, "rw");
91 }
92 
93 static bool ioport_event_begin(struct perf_sample *sample,
94 			       struct event_key *key)
95 {
96 	if (evsel__name_is(sample->evsel, "kvm:kvm_pio")) {
97 		ioport_event_get_key(sample, key);
98 		return true;
99 	}
100 
101 	return false;
102 }
103 
104 static bool ioport_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused)
105 {
106 	return kvm_entry_event(sample->evsel);
107 }
108 
109 static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
110 				    struct event_key *key,
111 				    char *decode)
112 {
113 	scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s",
114 		  (unsigned long long)key->key,
115 		  key->info ? "POUT" : "PIN");
116 }
117 
118 static const struct kvm_events_ops ioport_events = {
119 	.is_begin_event = ioport_event_begin,
120 	.is_end_event = ioport_event_end,
121 	.decode_key = ioport_event_decode_key,
122 	.name = "IO Port Access"
123 };
124 
125  /* The time of emulation msr is from kvm_msr to kvm_entry. */
126 static void msr_event_get_key(struct perf_sample *sample, struct event_key *key)
127 {
128 	key->key  = perf_sample__intval(sample, "ecx");
129 	key->info = perf_sample__intval(sample, "write");
130 }
131 
132 static bool msr_event_begin(struct perf_sample *sample, struct event_key *key)
133 {
134 	if (evsel__name_is(sample->evsel, "kvm:kvm_msr")) {
135 		msr_event_get_key(sample, key);
136 		return true;
137 	}
138 
139 	return false;
140 }
141 
142 static bool msr_event_end(struct perf_sample *sample, struct event_key *key __maybe_unused)
143 {
144 	return kvm_entry_event(sample->evsel);
145 }
146 
147 static void msr_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
148 				    struct event_key *key,
149 				    char *decode)
150 {
151 	scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s",
152 		  (unsigned long long)key->key,
153 		  key->info ? "W" : "R");
154 }
155 
156 static const struct kvm_events_ops msr_events = {
157 	.is_begin_event = msr_event_begin,
158 	.is_end_event = msr_event_end,
159 	.decode_key = msr_event_decode_key,
160 	.name = "MSR Access"
161 };
162 
163 static const char * const __kvm_events_tp[] = {
164 	"kvm:kvm_entry",
165 	"kvm:kvm_exit",
166 	"kvm:kvm_mmio",
167 	"kvm:kvm_pio",
168 	"kvm:kvm_msr",
169 	NULL,
170 };
171 
172 static const struct kvm_reg_events_ops __kvm_reg_events_ops[] = {
173 	{ .name = "vmexit", .ops = &exit_events },
174 	{ .name = "mmio", .ops = &mmio_events },
175 	{ .name = "ioport", .ops = &ioport_events },
176 	{ .name = "msr", .ops = &msr_events },
177 	{ NULL, NULL },
178 };
179 
180 static const char * const __kvm_skip_events[] = {
181 	"HLT",
182 	NULL,
183 };
184 
185 int __cpu_isa_init_x86(struct perf_kvm_stat *kvm, const char *cpuid)
186 {
187 	if (strstr(cpuid, "Intel")) {
188 		kvm->exit_reasons = vmx_exit_reasons;
189 		kvm->exit_reasons_isa = "VMX";
190 	} else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) {
191 		kvm->exit_reasons = svm_exit_reasons;
192 		kvm->exit_reasons_isa = "SVM";
193 	} else
194 		return -ENOTSUP;
195 
196 	return 0;
197 }
198 
199 /*
200  * After KVM supports PEBS for guest on Intel platforms
201  * (https://lore.kernel.org/all/20220411101946.20262-1-likexu@tencent.com/),
202  * host loses the capability to sample guest with PEBS since all PEBS related
203  * MSRs are switched to guest value after vm-entry, like IA32_DS_AREA MSR is
204  * switched to guest GVA at vm-entry. This would lead to "perf kvm record"
205  * fails to sample guest on Intel platforms since "cycles:P" event is used to
206  * sample guest by default.
207  *
208  * So, to avoid this issue explicitly use "cycles" instead of "cycles:P" event
209  * by default to sample guest on Intel platforms.
210  */
211 int __kvm_add_default_arch_event_x86(int *argc, const char **argv)
212 {
213 	int j = *argc;
214 
215 	argv[j++] = "-e";
216 	argv[j++] = "cycles";
217 	*argc += 2;
218 
219 	return 0;
220 }
221 
222 const char * const *__kvm_events_tp_x86(void)
223 {
224 	return __kvm_events_tp;
225 }
226 
227 const struct kvm_reg_events_ops *__kvm_reg_events_ops_x86(void)
228 {
229 	return __kvm_reg_events_ops;
230 }
231 
232 const char * const *__kvm_skip_events_x86(void)
233 {
234 	return __kvm_skip_events;
235 }
236