diff options
Diffstat (limited to 'tools/perf/builtin-kvm.c')
-rw-r--r-- | tools/perf/builtin-kvm.c | 414 |
1 files changed, 121 insertions, 293 deletions
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 0f1e5a2f6ad7..43367eb00510 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -29,114 +29,25 @@ | |||
29 | #include <pthread.h> | 29 | #include <pthread.h> |
30 | #include <math.h> | 30 | #include <math.h> |
31 | 31 | ||
32 | #if defined(__i386__) || defined(__x86_64__) | 32 | #ifdef HAVE_KVM_STAT_SUPPORT |
33 | #include <asm/svm.h> | 33 | #include <asm/kvm_perf.h> |
34 | #include <asm/vmx.h> | 34 | #include "util/kvm-stat.h" |
35 | #include <asm/kvm.h> | ||
36 | |||
37 | struct event_key { | ||
38 | #define INVALID_KEY (~0ULL) | ||
39 | u64 key; | ||
40 | int info; | ||
41 | }; | ||
42 | |||
43 | struct kvm_event_stats { | ||
44 | u64 time; | ||
45 | struct stats stats; | ||
46 | }; | ||
47 | |||
48 | struct kvm_event { | ||
49 | struct list_head hash_entry; | ||
50 | struct rb_node rb; | ||
51 | |||
52 | struct event_key key; | ||
53 | |||
54 | struct kvm_event_stats total; | ||
55 | |||
56 | #define DEFAULT_VCPU_NUM 8 | ||
57 | int max_vcpu; | ||
58 | struct kvm_event_stats *vcpu; | ||
59 | }; | ||
60 | |||
61 | typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); | ||
62 | |||
63 | struct kvm_event_key { | ||
64 | const char *name; | ||
65 | key_cmp_fun key; | ||
66 | }; | ||
67 | |||
68 | |||
69 | struct perf_kvm_stat; | ||
70 | |||
71 | struct kvm_events_ops { | ||
72 | bool (*is_begin_event)(struct perf_evsel *evsel, | ||
73 | struct perf_sample *sample, | ||
74 | struct event_key *key); | ||
75 | bool (*is_end_event)(struct perf_evsel *evsel, | ||
76 | struct perf_sample *sample, struct event_key *key); | ||
77 | void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, | ||
78 | char decode[20]); | ||
79 | const char *name; | ||
80 | }; | ||
81 | |||
82 | struct exit_reasons_table { | ||
83 | unsigned long exit_code; | ||
84 | const char *reason; | ||
85 | }; | ||
86 | 35 | ||
87 | #define EVENTS_BITS 12 | 36 | void exit_event_get_key(struct perf_evsel *evsel, |
88 | #define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) | 37 | struct perf_sample *sample, |
89 | 38 | struct event_key *key) | |
90 | struct perf_kvm_stat { | ||
91 | struct perf_tool tool; | ||
92 | struct record_opts opts; | ||
93 | struct perf_evlist *evlist; | ||
94 | struct perf_session *session; | ||
95 | |||
96 | const char *file_name; | ||
97 | const char *report_event; | ||
98 | const char *sort_key; | ||
99 | int trace_vcpu; | ||
100 | |||
101 | struct exit_reasons_table *exit_reasons; | ||
102 | int exit_reasons_size; | ||
103 | const char *exit_reasons_isa; | ||
104 | |||
105 | struct kvm_events_ops *events_ops; | ||
106 | key_cmp_fun compare; | ||
107 | struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; | ||
108 | |||
109 | u64 total_time; | ||
110 | u64 total_count; | ||
111 | u64 lost_events; | ||
112 | u64 duration; | ||
113 | |||
114 | const char *pid_str; | ||
115 | struct intlist *pid_list; | ||
116 | |||
117 | struct rb_root result; | ||
118 | |||
119 | int timerfd; | ||
120 | unsigned int display_time; | ||
121 | bool live; | ||
122 | }; | ||
123 | |||
124 | |||
125 | static void exit_event_get_key(struct perf_evsel *evsel, | ||
126 | struct perf_sample *sample, | ||
127 | struct event_key *key) | ||
128 | { | 39 | { |
129 | key->info = 0; | 40 | key->info = 0; |
130 | key->key = perf_evsel__intval(evsel, sample, "exit_reason"); | 41 | key->key = perf_evsel__intval(evsel, sample, KVM_EXIT_REASON); |
131 | } | 42 | } |
132 | 43 | ||
133 | static bool kvm_exit_event(struct perf_evsel *evsel) | 44 | bool kvm_exit_event(struct perf_evsel *evsel) |
134 | { | 45 | { |
135 | return !strcmp(evsel->name, "kvm:kvm_exit"); | 46 | return !strcmp(evsel->name, KVM_EXIT_TRACE); |
136 | } | 47 | } |
137 | 48 | ||
138 | static bool exit_event_begin(struct perf_evsel *evsel, | 49 | bool exit_event_begin(struct perf_evsel *evsel, |
139 | struct perf_sample *sample, struct event_key *key) | 50 | struct perf_sample *sample, struct event_key *key) |
140 | { | 51 | { |
141 | if (kvm_exit_event(evsel)) { | 52 | if (kvm_exit_event(evsel)) { |
142 | exit_event_get_key(evsel, sample, key); | 53 | exit_event_get_key(evsel, sample, key); |
@@ -146,32 +57,23 @@ static bool exit_event_begin(struct perf_evsel *evsel, | |||
146 | return false; | 57 | return false; |
147 | } | 58 | } |
148 | 59 | ||
149 | static bool kvm_entry_event(struct perf_evsel *evsel) | 60 | bool kvm_entry_event(struct perf_evsel *evsel) |
150 | { | 61 | { |
151 | return !strcmp(evsel->name, "kvm:kvm_entry"); | 62 | return !strcmp(evsel->name, KVM_ENTRY_TRACE); |
152 | } | 63 | } |
153 | 64 | ||
154 | static bool exit_event_end(struct perf_evsel *evsel, | 65 | bool exit_event_end(struct perf_evsel *evsel, |
155 | struct perf_sample *sample __maybe_unused, | 66 | struct perf_sample *sample __maybe_unused, |
156 | struct event_key *key __maybe_unused) | 67 | struct event_key *key __maybe_unused) |
157 | { | 68 | { |
158 | return kvm_entry_event(evsel); | 69 | return kvm_entry_event(evsel); |
159 | } | 70 | } |
160 | 71 | ||
161 | static struct exit_reasons_table vmx_exit_reasons[] = { | 72 | static const char *get_exit_reason(struct perf_kvm_stat *kvm, |
162 | VMX_EXIT_REASONS | 73 | struct exit_reasons_table *tbl, |
163 | }; | 74 | u64 exit_code) |
164 | |||
165 | static struct exit_reasons_table svm_exit_reasons[] = { | ||
166 | SVM_EXIT_REASONS | ||
167 | }; | ||
168 | |||
169 | static const char *get_exit_reason(struct perf_kvm_stat *kvm, u64 exit_code) | ||
170 | { | 75 | { |
171 | int i = kvm->exit_reasons_size; | 76 | while (tbl->reason != NULL) { |
172 | struct exit_reasons_table *tbl = kvm->exit_reasons; | ||
173 | |||
174 | while (i--) { | ||
175 | if (tbl->exit_code == exit_code) | 77 | if (tbl->exit_code == exit_code) |
176 | return tbl->reason; | 78 | return tbl->reason; |
177 | tbl++; | 79 | tbl++; |
@@ -182,148 +84,30 @@ static const char *get_exit_reason(struct perf_kvm_stat *kvm, u64 exit_code) | |||
182 | return "UNKNOWN"; | 84 | return "UNKNOWN"; |
183 | } | 85 | } |
184 | 86 | ||
185 | static void exit_event_decode_key(struct perf_kvm_stat *kvm, | 87 | void exit_event_decode_key(struct perf_kvm_stat *kvm, |
186 | struct event_key *key, | 88 | struct event_key *key, |
187 | char decode[20]) | 89 | char *decode) |
188 | { | 90 | { |
189 | const char *exit_reason = get_exit_reason(kvm, key->key); | 91 | const char *exit_reason = get_exit_reason(kvm, key->exit_reasons, |
92 | key->key); | ||
190 | 93 | ||
191 | scnprintf(decode, 20, "%s", exit_reason); | 94 | scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason); |
192 | } | 95 | } |
193 | 96 | ||
194 | static struct kvm_events_ops exit_events = { | 97 | static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) |
195 | .is_begin_event = exit_event_begin, | ||
196 | .is_end_event = exit_event_end, | ||
197 | .decode_key = exit_event_decode_key, | ||
198 | .name = "VM-EXIT" | ||
199 | }; | ||
200 | |||
201 | /* | ||
202 | * For the mmio events, we treat: | ||
203 | * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry | ||
204 | * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). | ||
205 | */ | ||
206 | static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, | ||
207 | struct event_key *key) | ||
208 | { | ||
209 | key->key = perf_evsel__intval(evsel, sample, "gpa"); | ||
210 | key->info = perf_evsel__intval(evsel, sample, "type"); | ||
211 | } | ||
212 | |||
213 | #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 | ||
214 | #define KVM_TRACE_MMIO_READ 1 | ||
215 | #define KVM_TRACE_MMIO_WRITE 2 | ||
216 | |||
217 | static bool mmio_event_begin(struct perf_evsel *evsel, | ||
218 | struct perf_sample *sample, struct event_key *key) | ||
219 | { | ||
220 | /* MMIO read begin event in kernel. */ | ||
221 | if (kvm_exit_event(evsel)) | ||
222 | return true; | ||
223 | |||
224 | /* MMIO write begin event in kernel. */ | ||
225 | if (!strcmp(evsel->name, "kvm:kvm_mmio") && | ||
226 | perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { | ||
227 | mmio_event_get_key(evsel, sample, key); | ||
228 | return true; | ||
229 | } | ||
230 | |||
231 | return false; | ||
232 | } | ||
233 | |||
234 | static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, | ||
235 | struct event_key *key) | ||
236 | { | ||
237 | /* MMIO write end event in kernel. */ | ||
238 | if (kvm_entry_event(evsel)) | ||
239 | return true; | ||
240 | |||
241 | /* MMIO read end event in kernel.*/ | ||
242 | if (!strcmp(evsel->name, "kvm:kvm_mmio") && | ||
243 | perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { | ||
244 | mmio_event_get_key(evsel, sample, key); | ||
245 | return true; | ||
246 | } | ||
247 | |||
248 | return false; | ||
249 | } | ||
250 | |||
251 | static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | ||
252 | struct event_key *key, | ||
253 | char decode[20]) | ||
254 | { | ||
255 | scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key, | ||
256 | key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); | ||
257 | } | ||
258 | |||
259 | static struct kvm_events_ops mmio_events = { | ||
260 | .is_begin_event = mmio_event_begin, | ||
261 | .is_end_event = mmio_event_end, | ||
262 | .decode_key = mmio_event_decode_key, | ||
263 | .name = "MMIO Access" | ||
264 | }; | ||
265 | |||
266 | /* The time of emulation pio access is from kvm_pio to kvm_entry. */ | ||
267 | static void ioport_event_get_key(struct perf_evsel *evsel, | ||
268 | struct perf_sample *sample, | ||
269 | struct event_key *key) | ||
270 | { | 98 | { |
271 | key->key = perf_evsel__intval(evsel, sample, "port"); | 99 | struct kvm_reg_events_ops *events_ops = kvm_reg_events_ops; |
272 | key->info = perf_evsel__intval(evsel, sample, "rw"); | ||
273 | } | ||
274 | 100 | ||
275 | static bool ioport_event_begin(struct perf_evsel *evsel, | 101 | for (events_ops = kvm_reg_events_ops; events_ops->name; events_ops++) { |
276 | struct perf_sample *sample, | 102 | if (!strcmp(events_ops->name, kvm->report_event)) { |
277 | struct event_key *key) | 103 | kvm->events_ops = events_ops->ops; |
278 | { | 104 | return true; |
279 | if (!strcmp(evsel->name, "kvm:kvm_pio")) { | 105 | } |
280 | ioport_event_get_key(evsel, sample, key); | ||
281 | return true; | ||
282 | } | 106 | } |
283 | 107 | ||
284 | return false; | 108 | return false; |
285 | } | 109 | } |
286 | 110 | ||
287 | static bool ioport_event_end(struct perf_evsel *evsel, | ||
288 | struct perf_sample *sample __maybe_unused, | ||
289 | struct event_key *key __maybe_unused) | ||
290 | { | ||
291 | return kvm_entry_event(evsel); | ||
292 | } | ||
293 | |||
294 | static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | ||
295 | struct event_key *key, | ||
296 | char decode[20]) | ||
297 | { | ||
298 | scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key, | ||
299 | key->info ? "POUT" : "PIN"); | ||
300 | } | ||
301 | |||
302 | static struct kvm_events_ops ioport_events = { | ||
303 | .is_begin_event = ioport_event_begin, | ||
304 | .is_end_event = ioport_event_end, | ||
305 | .decode_key = ioport_event_decode_key, | ||
306 | .name = "IO Port Access" | ||
307 | }; | ||
308 | |||
309 | static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) | ||
310 | { | ||
311 | bool ret = true; | ||
312 | |||
313 | if (!strcmp(kvm->report_event, "vmexit")) | ||
314 | kvm->events_ops = &exit_events; | ||
315 | else if (!strcmp(kvm->report_event, "mmio")) | ||
316 | kvm->events_ops = &mmio_events; | ||
317 | else if (!strcmp(kvm->report_event, "ioport")) | ||
318 | kvm->events_ops = &ioport_events; | ||
319 | else { | ||
320 | pr_err("Unknown report event:%s\n", kvm->report_event); | ||
321 | ret = false; | ||
322 | } | ||
323 | |||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | struct vcpu_event_record { | 111 | struct vcpu_event_record { |
328 | int vcpu_id; | 112 | int vcpu_id; |
329 | u64 start_time; | 113 | u64 start_time; |
@@ -477,6 +261,54 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, | |||
477 | return true; | 261 | return true; |
478 | } | 262 | } |
479 | 263 | ||
264 | static bool is_child_event(struct perf_kvm_stat *kvm, | ||
265 | struct perf_evsel *evsel, | ||
266 | struct perf_sample *sample, | ||
267 | struct event_key *key) | ||
268 | { | ||
269 | struct child_event_ops *child_ops; | ||
270 | |||
271 | child_ops = kvm->events_ops->child_ops; | ||
272 | |||
273 | if (!child_ops) | ||
274 | return false; | ||
275 | |||
276 | for (; child_ops->name; child_ops++) { | ||
277 | if (!strcmp(evsel->name, child_ops->name)) { | ||
278 | child_ops->get_key(evsel, sample, key); | ||
279 | return true; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | return false; | ||
284 | } | ||
285 | |||
286 | static bool handle_child_event(struct perf_kvm_stat *kvm, | ||
287 | struct vcpu_event_record *vcpu_record, | ||
288 | struct event_key *key, | ||
289 | struct perf_sample *sample __maybe_unused) | ||
290 | { | ||
291 | struct kvm_event *event = NULL; | ||
292 | |||
293 | if (key->key != INVALID_KEY) | ||
294 | event = find_create_kvm_event(kvm, key); | ||
295 | |||
296 | vcpu_record->last_event = event; | ||
297 | |||
298 | return true; | ||
299 | } | ||
300 | |||
301 | static bool skip_event(const char *event) | ||
302 | { | ||
303 | const char * const *skip_events; | ||
304 | |||
305 | for (skip_events = kvm_skip_events; *skip_events; skip_events++) | ||
306 | if (!strcmp(event, *skip_events)) | ||
307 | return true; | ||
308 | |||
309 | return false; | ||
310 | } | ||
311 | |||
480 | static bool handle_end_event(struct perf_kvm_stat *kvm, | 312 | static bool handle_end_event(struct perf_kvm_stat *kvm, |
481 | struct vcpu_event_record *vcpu_record, | 313 | struct vcpu_event_record *vcpu_record, |
482 | struct event_key *key, | 314 | struct event_key *key, |
@@ -525,10 +357,10 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, | |||
525 | time_diff = sample->time - time_begin; | 357 | time_diff = sample->time - time_begin; |
526 | 358 | ||
527 | if (kvm->duration && time_diff > kvm->duration) { | 359 | if (kvm->duration && time_diff > kvm->duration) { |
528 | char decode[32]; | 360 | char decode[DECODE_STR_LEN]; |
529 | 361 | ||
530 | kvm->events_ops->decode_key(kvm, &event->key, decode); | 362 | kvm->events_ops->decode_key(kvm, &event->key, decode); |
531 | if (strcmp(decode, "HLT")) { | 363 | if (!skip_event(decode)) { |
532 | pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n", | 364 | pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n", |
533 | sample->time, sample->pid, vcpu_record->vcpu_id, | 365 | sample->time, sample->pid, vcpu_record->vcpu_id, |
534 | decode, time_diff/1000); | 366 | decode, time_diff/1000); |
@@ -553,7 +385,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
553 | return NULL; | 385 | return NULL; |
554 | } | 386 | } |
555 | 387 | ||
556 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, "vcpu_id"); | 388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); |
557 | thread->priv = vcpu_record; | 389 | thread->priv = vcpu_record; |
558 | } | 390 | } |
559 | 391 | ||
@@ -566,7 +398,8 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, | |||
566 | struct perf_sample *sample) | 398 | struct perf_sample *sample) |
567 | { | 399 | { |
568 | struct vcpu_event_record *vcpu_record; | 400 | struct vcpu_event_record *vcpu_record; |
569 | struct event_key key = {.key = INVALID_KEY}; | 401 | struct event_key key = { .key = INVALID_KEY, |
402 | .exit_reasons = kvm->exit_reasons }; | ||
570 | 403 | ||
571 | vcpu_record = per_vcpu_record(thread, evsel, sample); | 404 | vcpu_record = per_vcpu_record(thread, evsel, sample); |
572 | if (!vcpu_record) | 405 | if (!vcpu_record) |
@@ -580,6 +413,9 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, | |||
580 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) | 413 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) |
581 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); | 414 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); |
582 | 415 | ||
416 | if (is_child_event(kvm, evsel, sample, &key)) | ||
417 | return handle_child_event(kvm, vcpu_record, &key, sample); | ||
418 | |||
583 | if (kvm->events_ops->is_end_event(evsel, sample, &key)) | 419 | if (kvm->events_ops->is_end_event(evsel, sample, &key)) |
584 | return handle_end_event(kvm, vcpu_record, &key, sample); | 420 | return handle_end_event(kvm, vcpu_record, &key, sample); |
585 | 421 | ||
@@ -740,7 +576,7 @@ static void show_timeofday(void) | |||
740 | 576 | ||
741 | static void print_result(struct perf_kvm_stat *kvm) | 577 | static void print_result(struct perf_kvm_stat *kvm) |
742 | { | 578 | { |
743 | char decode[20]; | 579 | char decode[DECODE_STR_LEN]; |
744 | struct kvm_event *event; | 580 | struct kvm_event *event; |
745 | int vcpu = kvm->trace_vcpu; | 581 | int vcpu = kvm->trace_vcpu; |
746 | 582 | ||
@@ -751,7 +587,7 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
751 | 587 | ||
752 | pr_info("\n\n"); | 588 | pr_info("\n\n"); |
753 | print_vcpu_info(kvm); | 589 | print_vcpu_info(kvm); |
754 | pr_info("%20s ", kvm->events_ops->name); | 590 | pr_info("%*s ", DECODE_STR_LEN, kvm->events_ops->name); |
755 | pr_info("%10s ", "Samples"); | 591 | pr_info("%10s ", "Samples"); |
756 | pr_info("%9s ", "Samples%"); | 592 | pr_info("%9s ", "Samples%"); |
757 | 593 | ||
@@ -770,7 +606,7 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
770 | min = get_event_min(event, vcpu); | 606 | min = get_event_min(event, vcpu); |
771 | 607 | ||
772 | kvm->events_ops->decode_key(kvm, &event->key, decode); | 608 | kvm->events_ops->decode_key(kvm, &event->key, decode); |
773 | pr_info("%20s ", decode); | 609 | pr_info("%*s ", DECODE_STR_LEN, decode); |
774 | pr_info("%10llu ", (unsigned long long)ecount); | 610 | pr_info("%10llu ", (unsigned long long)ecount); |
775 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); | 611 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); |
776 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); | 612 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); |
@@ -839,34 +675,28 @@ static int process_sample_event(struct perf_tool *tool, | |||
839 | static int cpu_isa_config(struct perf_kvm_stat *kvm) | 675 | static int cpu_isa_config(struct perf_kvm_stat *kvm) |
840 | { | 676 | { |
841 | char buf[64], *cpuid; | 677 | char buf[64], *cpuid; |
842 | int err, isa; | 678 | int err; |
843 | 679 | ||
844 | if (kvm->live) { | 680 | if (kvm->live) { |
845 | err = get_cpuid(buf, sizeof(buf)); | 681 | err = get_cpuid(buf, sizeof(buf)); |
846 | if (err != 0) { | 682 | if (err != 0) { |
847 | pr_err("Failed to look up CPU type (Intel or AMD)\n"); | 683 | pr_err("Failed to look up CPU type\n"); |
848 | return err; | 684 | return err; |
849 | } | 685 | } |
850 | cpuid = buf; | 686 | cpuid = buf; |
851 | } else | 687 | } else |
852 | cpuid = kvm->session->header.env.cpuid; | 688 | cpuid = kvm->session->header.env.cpuid; |
853 | 689 | ||
854 | if (strstr(cpuid, "Intel")) | 690 | if (!cpuid) { |
855 | isa = 1; | 691 | pr_err("Failed to look up CPU type\n"); |
856 | else if (strstr(cpuid, "AMD")) | 692 | return -EINVAL; |
857 | isa = 0; | ||
858 | else { | ||
859 | pr_err("CPU %s is not supported.\n", cpuid); | ||
860 | return -ENOTSUP; | ||
861 | } | 693 | } |
862 | 694 | ||
863 | if (isa == 1) { | 695 | err = cpu_isa_init(kvm, cpuid); |
864 | kvm->exit_reasons = vmx_exit_reasons; | 696 | if (err == -ENOTSUP) |
865 | kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons); | 697 | pr_err("CPU %s is not supported.\n", cpuid); |
866 | kvm->exit_reasons_isa = "VMX"; | ||
867 | } | ||
868 | 698 | ||
869 | return 0; | 699 | return err; |
870 | } | 700 | } |
871 | 701 | ||
872 | static bool verify_vcpu(int vcpu) | 702 | static bool verify_vcpu(int vcpu) |
@@ -1300,13 +1130,6 @@ exit: | |||
1300 | return ret; | 1130 | return ret; |
1301 | } | 1131 | } |
1302 | 1132 | ||
1303 | static const char * const kvm_events_tp[] = { | ||
1304 | "kvm:kvm_entry", | ||
1305 | "kvm:kvm_exit", | ||
1306 | "kvm:kvm_mmio", | ||
1307 | "kvm:kvm_pio", | ||
1308 | }; | ||
1309 | |||
1310 | #define STRDUP_FAIL_EXIT(s) \ | 1133 | #define STRDUP_FAIL_EXIT(s) \ |
1311 | ({ char *_p; \ | 1134 | ({ char *_p; \ |
1312 | _p = strdup(s); \ | 1135 | _p = strdup(s); \ |
@@ -1318,7 +1141,7 @@ static const char * const kvm_events_tp[] = { | |||
1318 | static int | 1141 | static int |
1319 | kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | 1142 | kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) |
1320 | { | 1143 | { |
1321 | unsigned int rec_argc, i, j; | 1144 | unsigned int rec_argc, i, j, events_tp_size; |
1322 | const char **rec_argv; | 1145 | const char **rec_argv; |
1323 | const char * const record_args[] = { | 1146 | const char * const record_args[] = { |
1324 | "record", | 1147 | "record", |
@@ -1326,9 +1149,14 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1326 | "-m", "1024", | 1149 | "-m", "1024", |
1327 | "-c", "1", | 1150 | "-c", "1", |
1328 | }; | 1151 | }; |
1152 | const char * const *events_tp; | ||
1153 | events_tp_size = 0; | ||
1154 | |||
1155 | for (events_tp = kvm_events_tp; *events_tp; events_tp++) | ||
1156 | events_tp_size++; | ||
1329 | 1157 | ||
1330 | rec_argc = ARRAY_SIZE(record_args) + argc + 2 + | 1158 | rec_argc = ARRAY_SIZE(record_args) + argc + 2 + |
1331 | 2 * ARRAY_SIZE(kvm_events_tp); | 1159 | 2 * events_tp_size; |
1332 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1160 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
1333 | 1161 | ||
1334 | if (rec_argv == NULL) | 1162 | if (rec_argv == NULL) |
@@ -1337,7 +1165,7 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1337 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1165 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
1338 | rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); | 1166 | rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); |
1339 | 1167 | ||
1340 | for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { | 1168 | for (j = 0; j < events_tp_size; j++) { |
1341 | rec_argv[i++] = "-e"; | 1169 | rec_argv[i++] = "-e"; |
1342 | rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]); | 1170 | rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]); |
1343 | } | 1171 | } |
@@ -1356,7 +1184,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1356 | { | 1184 | { |
1357 | const struct option kvm_events_report_options[] = { | 1185 | const struct option kvm_events_report_options[] = { |
1358 | OPT_STRING(0, "event", &kvm->report_event, "report event", | 1186 | OPT_STRING(0, "event", &kvm->report_event, "report event", |
1359 | "event for reporting: vmexit, mmio, ioport"), | 1187 | "event for reporting: vmexit, " |
1188 | "mmio (x86 only), ioport (x86 only)"), | ||
1360 | OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, | 1189 | OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, |
1361 | "vcpu id to report"), | 1190 | "vcpu id to report"), |
1362 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1191 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
@@ -1391,16 +1220,16 @@ static struct perf_evlist *kvm_live_event_list(void) | |||
1391 | { | 1220 | { |
1392 | struct perf_evlist *evlist; | 1221 | struct perf_evlist *evlist; |
1393 | char *tp, *name, *sys; | 1222 | char *tp, *name, *sys; |
1394 | unsigned int j; | ||
1395 | int err = -1; | 1223 | int err = -1; |
1224 | const char * const *events_tp; | ||
1396 | 1225 | ||
1397 | evlist = perf_evlist__new(); | 1226 | evlist = perf_evlist__new(); |
1398 | if (evlist == NULL) | 1227 | if (evlist == NULL) |
1399 | return NULL; | 1228 | return NULL; |
1400 | 1229 | ||
1401 | for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { | 1230 | for (events_tp = kvm_events_tp; *events_tp; events_tp++) { |
1402 | 1231 | ||
1403 | tp = strdup(kvm_events_tp[j]); | 1232 | tp = strdup(*events_tp); |
1404 | if (tp == NULL) | 1233 | if (tp == NULL) |
1405 | goto out; | 1234 | goto out; |
1406 | 1235 | ||
@@ -1409,7 +1238,7 @@ static struct perf_evlist *kvm_live_event_list(void) | |||
1409 | name = strchr(tp, ':'); | 1238 | name = strchr(tp, ':'); |
1410 | if (name == NULL) { | 1239 | if (name == NULL) { |
1411 | pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n", | 1240 | pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n", |
1412 | kvm_events_tp[j]); | 1241 | *events_tp); |
1413 | free(tp); | 1242 | free(tp); |
1414 | goto out; | 1243 | goto out; |
1415 | } | 1244 | } |
@@ -1417,7 +1246,7 @@ static struct perf_evlist *kvm_live_event_list(void) | |||
1417 | name++; | 1246 | name++; |
1418 | 1247 | ||
1419 | if (perf_evlist__add_newtp(evlist, sys, name, NULL)) { | 1248 | if (perf_evlist__add_newtp(evlist, sys, name, NULL)) { |
1420 | pr_err("Failed to add %s tracepoint to the list\n", kvm_events_tp[j]); | 1249 | pr_err("Failed to add %s tracepoint to the list\n", *events_tp); |
1421 | free(tp); | 1250 | free(tp); |
1422 | goto out; | 1251 | goto out; |
1423 | } | 1252 | } |
@@ -1462,7 +1291,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1462 | "key for sorting: sample(sort by samples number)" | 1291 | "key for sorting: sample(sort by samples number)" |
1463 | " time (sort by avg time)"), | 1292 | " time (sort by avg time)"), |
1464 | OPT_U64(0, "duration", &kvm->duration, | 1293 | OPT_U64(0, "duration", &kvm->duration, |
1465 | "show events other than HALT that take longer than duration usecs"), | 1294 | "show events other than" |
1295 | " HLT (x86 only) or Wait state (s390 only)" | ||
1296 | " that take longer than duration usecs"), | ||
1466 | OPT_END() | 1297 | OPT_END() |
1467 | }; | 1298 | }; |
1468 | const char * const live_usage[] = { | 1299 | const char * const live_usage[] = { |
@@ -1585,9 +1416,6 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv) | |||
1585 | .report_event = "vmexit", | 1416 | .report_event = "vmexit", |
1586 | .sort_key = "sample", | 1417 | .sort_key = "sample", |
1587 | 1418 | ||
1588 | .exit_reasons = svm_exit_reasons, | ||
1589 | .exit_reasons_size = ARRAY_SIZE(svm_exit_reasons), | ||
1590 | .exit_reasons_isa = "SVM", | ||
1591 | }; | 1419 | }; |
1592 | 1420 | ||
1593 | if (argc == 1) { | 1421 | if (argc == 1) { |
@@ -1609,7 +1437,7 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv) | |||
1609 | perf_stat: | 1437 | perf_stat: |
1610 | return cmd_stat(argc, argv, NULL); | 1438 | return cmd_stat(argc, argv, NULL); |
1611 | } | 1439 | } |
1612 | #endif | 1440 | #endif /* HAVE_KVM_STAT_SUPPORT */ |
1613 | 1441 | ||
1614 | static int __cmd_record(const char *file_name, int argc, const char **argv) | 1442 | static int __cmd_record(const char *file_name, int argc, const char **argv) |
1615 | { | 1443 | { |
@@ -1726,7 +1554,7 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1726 | return cmd_top(argc, argv, NULL); | 1554 | return cmd_top(argc, argv, NULL); |
1727 | else if (!strncmp(argv[0], "buildid-list", 12)) | 1555 | else if (!strncmp(argv[0], "buildid-list", 12)) |
1728 | return __cmd_buildid_list(file_name, argc, argv); | 1556 | return __cmd_buildid_list(file_name, argc, argv); |
1729 | #if defined(__i386__) || defined(__x86_64__) | 1557 | #ifdef HAVE_KVM_STAT_SUPPORT |
1730 | else if (!strncmp(argv[0], "stat", 4)) | 1558 | else if (!strncmp(argv[0], "stat", 4)) |
1731 | return kvm_cmd_stat(file_name, argc, argv); | 1559 | return kvm_cmd_stat(file_name, argc, argv); |
1732 | #endif | 1560 | #endif |