1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include "util/kvm-stat.h" 4 #include "util/parse-events.h" 5 #include "util/debug.h" 6 #include "util/evsel.h" 7 #include "util/evlist.h" 8 9 #include "book3s_hv_exits.h" 10 #include "book3s_hcalls.h" 11 12 #define NR_TPS 4 13 14 const char *vcpu_id_str = "vcpu_id"; 15 const int decode_str_len = 40; 16 const char *kvm_entry_trace = "kvm_hv:kvm_guest_enter"; 17 const char *kvm_exit_trace = "kvm_hv:kvm_guest_exit"; 18 19 define_exit_reasons_table(hv_exit_reasons, kvm_trace_symbol_exit); 20 define_exit_reasons_table(hcall_reasons, kvm_trace_symbol_hcall); 21 22 /* Tracepoints specific to ppc_book3s_hv */ 23 const char *ppc_book3s_hv_kvm_tp[] = { 24 "kvm_hv:kvm_guest_enter", 25 "kvm_hv:kvm_guest_exit", 26 "kvm_hv:kvm_hcall_enter", 27 "kvm_hv:kvm_hcall_exit", 28 NULL, 29 }; 30 31 /* 1 extra placeholder for NULL */ 32 const char *kvm_events_tp[NR_TPS + 1]; 33 const char *kvm_exit_reason; 34 35 static void hcall_event_get_key(struct perf_evsel *evsel, 36 struct perf_sample *sample, 37 struct event_key *key) 38 { 39 key->info = 0; 40 key->key = perf_evsel__intval(evsel, sample, "req"); 41 } 42 43 static const char *get_hcall_exit_reason(u64 exit_code) 44 { 45 struct exit_reasons_table *tbl = hcall_reasons; 46 47 while (tbl->reason != NULL) { 48 if (tbl->exit_code == exit_code) 49 return tbl->reason; 50 tbl++; 51 } 52 53 pr_debug("Unknown hcall code: %lld\n", 54 (unsigned long long)exit_code); 55 return "UNKNOWN"; 56 } 57 58 static bool hcall_event_end(struct perf_evsel *evsel, 59 struct perf_sample *sample __maybe_unused, 60 struct event_key *key __maybe_unused) 61 { 62 return (!strcmp(evsel->name, kvm_events_tp[3])); 63 } 64 65 static bool hcall_event_begin(struct perf_evsel *evsel, 66 struct perf_sample *sample, struct event_key *key) 67 { 68 if (!strcmp(evsel->name, kvm_events_tp[2])) { 69 hcall_event_get_key(evsel, sample, key); 70 return true; 71 } 72 73 return false; 74 } 75 static void hcall_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 76 struct event_key *key, 77 char *decode) 78 { 79 const char *hcall_reason = get_hcall_exit_reason(key->key); 80 81 scnprintf(decode, decode_str_len, "%s", hcall_reason); 82 } 83 84 static struct kvm_events_ops hcall_events = { 85 .is_begin_event = hcall_event_begin, 86 .is_end_event = hcall_event_end, 87 .decode_key = hcall_event_decode_key, 88 .name = "HCALL-EVENT", 89 }; 90 91 static struct kvm_events_ops exit_events = { 92 .is_begin_event = exit_event_begin, 93 .is_end_event = exit_event_end, 94 .decode_key = exit_event_decode_key, 95 .name = "VM-EXIT" 96 }; 97 98 struct kvm_reg_events_ops kvm_reg_events_ops[] = { 99 { .name = "vmexit", .ops = &exit_events }, 100 { .name = "hcall", .ops = &hcall_events }, 101 { NULL, NULL }, 102 }; 103 104 const char * const kvm_skip_events[] = { 105 NULL, 106 }; 107 108 109 static int is_tracepoint_available(const char *str, struct perf_evlist *evlist) 110 { 111 struct parse_events_error err; 112 int ret; 113 114 err.str = NULL; 115 ret = parse_events(evlist, str, &err); 116 if (err.str) 117 pr_err("%s : %s\n", str, err.str); 118 return ret; 119 } 120 121 static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm, 122 struct perf_evlist *evlist) 123 { 124 const char **events_ptr; 125 int i, nr_tp = 0, err = -1; 126 127 /* Check for book3s_hv tracepoints */ 128 for (events_ptr = ppc_book3s_hv_kvm_tp; *events_ptr; events_ptr++) { 129 err = is_tracepoint_available(*events_ptr, evlist); 130 if (err) 131 return -1; 132 nr_tp++; 133 } 134 135 for (i = 0; i < nr_tp; i++) 136 kvm_events_tp[i] = ppc_book3s_hv_kvm_tp[i]; 137 138 kvm_events_tp[i] = NULL; 139 kvm_exit_reason = "trap"; 140 kvm->exit_reasons = hv_exit_reasons; 141 kvm->exit_reasons_isa = "HV"; 142 143 return 0; 144 } 145 146 /* Wrapper to setup kvm tracepoints */ 147 static int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm) 148 { 149 struct perf_evlist *evlist = perf_evlist__new(); 150 151 if (evlist == NULL) 152 return -ENOMEM; 153 154 /* Right now, only supported on book3s_hv */ 155 return ppc__setup_book3s_hv(kvm, evlist); 156 } 157 158 int setup_kvm_events_tp(struct perf_kvm_stat *kvm) 159 { 160 return ppc__setup_kvm_tp(kvm); 161 } 162 163 int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) 164 { 165 int ret; 166 167 ret = ppc__setup_kvm_tp(kvm); 168 if (ret) { 169 kvm->exit_reasons = NULL; 170 kvm->exit_reasons_isa = NULL; 171 } 172 173 return ret; 174 } 175