Commit | Line | Data |
---|---|---|
9daa8123 | 1 | #include "../../util/kvm-stat.h" |
162607ea HK |
2 | #include <asm/svm.h> |
3 | #include <asm/vmx.h> | |
4 | #include <asm/kvm.h> | |
9daa8123 AY |
5 | |
6 | define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); | |
7 | define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); | |
8 | ||
9 | static struct kvm_events_ops exit_events = { | |
10 | .is_begin_event = exit_event_begin, | |
11 | .is_end_event = exit_event_end, | |
12 | .decode_key = exit_event_decode_key, | |
13 | .name = "VM-EXIT" | |
14 | }; | |
15 | ||
162607ea HK |
16 | const char *vcpu_id_str = "vcpu_id"; |
17 | const int decode_str_len = 20; | |
18 | const char *kvm_exit_reason = "exit_reason"; | |
19 | const char *kvm_entry_trace = "kvm:kvm_entry"; | |
20 | const char *kvm_exit_trace = "kvm:kvm_exit"; | |
21 | ||
9daa8123 AY |
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_evsel *evsel, struct perf_sample *sample, | |
28 | struct event_key *key) | |
29 | { | |
30 | key->key = perf_evsel__intval(evsel, sample, "gpa"); | |
31 | key->info = perf_evsel__intval(evsel, 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_evsel *evsel, | |
39 | struct perf_sample *sample, struct event_key *key) | |
40 | { | |
41 | /* MMIO read begin event in kernel. */ | |
42 | if (kvm_exit_event(evsel)) | |
43 | return true; | |
44 | ||
45 | /* MMIO write begin event in kernel. */ | |
46 | if (!strcmp(evsel->name, "kvm:kvm_mmio") && | |
47 | perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { | |
48 | mmio_event_get_key(evsel, sample, key); | |
49 | return true; | |
50 | } | |
51 | ||
52 | return false; | |
53 | } | |
54 | ||
55 | static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, | |
56 | struct event_key *key) | |
57 | { | |
58 | /* MMIO write end event in kernel. */ | |
59 | if (kvm_entry_event(evsel)) | |
60 | return true; | |
61 | ||
62 | /* MMIO read end event in kernel.*/ | |
63 | if (!strcmp(evsel->name, "kvm:kvm_mmio") && | |
64 | perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { | |
65 | mmio_event_get_key(evsel, sample, key); | |
66 | return true; | |
67 | } | |
68 | ||
69 | return false; | |
70 | } | |
71 | ||
72 | static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | |
73 | struct event_key *key, | |
74 | char *decode) | |
75 | { | |
162607ea | 76 | scnprintf(decode, decode_str_len, "%#lx:%s", |
9daa8123 AY |
77 | (unsigned long)key->key, |
78 | key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); | |
79 | } | |
80 | ||
81 | static struct kvm_events_ops mmio_events = { | |
82 | .is_begin_event = mmio_event_begin, | |
83 | .is_end_event = mmio_event_end, | |
84 | .decode_key = mmio_event_decode_key, | |
85 | .name = "MMIO Access" | |
86 | }; | |
87 | ||
88 | /* The time of emulation pio access is from kvm_pio to kvm_entry. */ | |
89 | static void ioport_event_get_key(struct perf_evsel *evsel, | |
90 | struct perf_sample *sample, | |
91 | struct event_key *key) | |
92 | { | |
93 | key->key = perf_evsel__intval(evsel, sample, "port"); | |
94 | key->info = perf_evsel__intval(evsel, sample, "rw"); | |
95 | } | |
96 | ||
97 | static bool ioport_event_begin(struct perf_evsel *evsel, | |
98 | struct perf_sample *sample, | |
99 | struct event_key *key) | |
100 | { | |
101 | if (!strcmp(evsel->name, "kvm:kvm_pio")) { | |
102 | ioport_event_get_key(evsel, sample, key); | |
103 | return true; | |
104 | } | |
105 | ||
106 | return false; | |
107 | } | |
108 | ||
109 | static bool ioport_event_end(struct perf_evsel *evsel, | |
110 | struct perf_sample *sample __maybe_unused, | |
111 | struct event_key *key __maybe_unused) | |
112 | { | |
113 | return kvm_entry_event(evsel); | |
114 | } | |
115 | ||
116 | static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | |
117 | struct event_key *key, | |
118 | char *decode) | |
119 | { | |
162607ea | 120 | scnprintf(decode, decode_str_len, "%#llx:%s", |
9daa8123 AY |
121 | (unsigned long long)key->key, |
122 | key->info ? "POUT" : "PIN"); | |
123 | } | |
124 | ||
125 | static struct kvm_events_ops ioport_events = { | |
126 | .is_begin_event = ioport_event_begin, | |
127 | .is_end_event = ioport_event_end, | |
128 | .decode_key = ioport_event_decode_key, | |
129 | .name = "IO Port Access" | |
130 | }; | |
131 | ||
48deaa74 | 132 | const char *kvm_events_tp[] = { |
9daa8123 AY |
133 | "kvm:kvm_entry", |
134 | "kvm:kvm_exit", | |
135 | "kvm:kvm_mmio", | |
136 | "kvm:kvm_pio", | |
137 | NULL, | |
138 | }; | |
139 | ||
140 | struct kvm_reg_events_ops kvm_reg_events_ops[] = { | |
141 | { .name = "vmexit", .ops = &exit_events }, | |
142 | { .name = "mmio", .ops = &mmio_events }, | |
143 | { .name = "ioport", .ops = &ioport_events }, | |
144 | { NULL, NULL }, | |
145 | }; | |
146 | ||
54c801ff AY |
147 | const char * const kvm_skip_events[] = { |
148 | "HLT", | |
149 | NULL, | |
150 | }; | |
151 | ||
9daa8123 AY |
152 | int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) |
153 | { | |
154 | if (strstr(cpuid, "Intel")) { | |
155 | kvm->exit_reasons = vmx_exit_reasons; | |
156 | kvm->exit_reasons_isa = "VMX"; | |
157 | } else if (strstr(cpuid, "AMD")) { | |
158 | kvm->exit_reasons = svm_exit_reasons; | |
159 | kvm->exit_reasons_isa = "SVM"; | |
160 | } else | |
161 | return -ENOTSUP; | |
162 | ||
163 | return 0; | |
164 | } |