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