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