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