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