162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <errno.h> 362306a36Sopenharmony_ci#include "util/kvm-stat.h" 462306a36Sopenharmony_ci#include "util/parse-events.h" 562306a36Sopenharmony_ci#include "util/debug.h" 662306a36Sopenharmony_ci#include "util/evsel.h" 762306a36Sopenharmony_ci#include "util/evlist.h" 862306a36Sopenharmony_ci#include "util/pmus.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "book3s_hv_exits.h" 1162306a36Sopenharmony_ci#include "book3s_hcalls.h" 1262306a36Sopenharmony_ci#include <subcmd/parse-options.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define NR_TPS 4 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciconst char *vcpu_id_str = "vcpu_id"; 1762306a36Sopenharmony_ciconst char *kvm_entry_trace = "kvm_hv:kvm_guest_enter"; 1862306a36Sopenharmony_ciconst char *kvm_exit_trace = "kvm_hv:kvm_guest_exit"; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cidefine_exit_reasons_table(hv_exit_reasons, kvm_trace_symbol_exit); 2162306a36Sopenharmony_cidefine_exit_reasons_table(hcall_reasons, kvm_trace_symbol_hcall); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Tracepoints specific to ppc_book3s_hv */ 2462306a36Sopenharmony_ciconst char *ppc_book3s_hv_kvm_tp[] = { 2562306a36Sopenharmony_ci "kvm_hv:kvm_guest_enter", 2662306a36Sopenharmony_ci "kvm_hv:kvm_guest_exit", 2762306a36Sopenharmony_ci "kvm_hv:kvm_hcall_enter", 2862306a36Sopenharmony_ci "kvm_hv:kvm_hcall_exit", 2962306a36Sopenharmony_ci NULL, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 1 extra placeholder for NULL */ 3362306a36Sopenharmony_ciconst char *kvm_events_tp[NR_TPS + 1]; 3462306a36Sopenharmony_ciconst char *kvm_exit_reason; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void hcall_event_get_key(struct evsel *evsel, 3762306a36Sopenharmony_ci struct perf_sample *sample, 3862306a36Sopenharmony_ci struct event_key *key) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci key->info = 0; 4162306a36Sopenharmony_ci key->key = evsel__intval(evsel, sample, "req"); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const char *get_hcall_exit_reason(u64 exit_code) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct exit_reasons_table *tbl = hcall_reasons; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci while (tbl->reason != NULL) { 4962306a36Sopenharmony_ci if (tbl->exit_code == exit_code) 5062306a36Sopenharmony_ci return tbl->reason; 5162306a36Sopenharmony_ci tbl++; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci pr_debug("Unknown hcall code: %lld\n", 5562306a36Sopenharmony_ci (unsigned long long)exit_code); 5662306a36Sopenharmony_ci return "UNKNOWN"; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool hcall_event_end(struct evsel *evsel, 6062306a36Sopenharmony_ci struct perf_sample *sample __maybe_unused, 6162306a36Sopenharmony_ci struct event_key *key __maybe_unused) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return (evsel__name_is(evsel, kvm_events_tp[3])); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic bool hcall_event_begin(struct evsel *evsel, 6762306a36Sopenharmony_ci struct perf_sample *sample, struct event_key *key) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci if (evsel__name_is(evsel, kvm_events_tp[2])) { 7062306a36Sopenharmony_ci hcall_event_get_key(evsel, sample, key); 7162306a36Sopenharmony_ci return true; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return false; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_cistatic void hcall_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 7762306a36Sopenharmony_ci struct event_key *key, 7862306a36Sopenharmony_ci char *decode) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci const char *hcall_reason = get_hcall_exit_reason(key->key); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci scnprintf(decode, KVM_EVENT_NAME_LEN, "%s", hcall_reason); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic struct kvm_events_ops hcall_events = { 8662306a36Sopenharmony_ci .is_begin_event = hcall_event_begin, 8762306a36Sopenharmony_ci .is_end_event = hcall_event_end, 8862306a36Sopenharmony_ci .decode_key = hcall_event_decode_key, 8962306a36Sopenharmony_ci .name = "HCALL-EVENT", 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic struct kvm_events_ops exit_events = { 9362306a36Sopenharmony_ci .is_begin_event = exit_event_begin, 9462306a36Sopenharmony_ci .is_end_event = exit_event_end, 9562306a36Sopenharmony_ci .decode_key = exit_event_decode_key, 9662306a36Sopenharmony_ci .name = "VM-EXIT" 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct kvm_reg_events_ops kvm_reg_events_ops[] = { 10062306a36Sopenharmony_ci { .name = "vmexit", .ops = &exit_events }, 10162306a36Sopenharmony_ci { .name = "hcall", .ops = &hcall_events }, 10262306a36Sopenharmony_ci { NULL, NULL }, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciconst char * const kvm_skip_events[] = { 10662306a36Sopenharmony_ci NULL, 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int is_tracepoint_available(const char *str, struct evlist *evlist) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct parse_events_error err; 11362306a36Sopenharmony_ci int ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci parse_events_error__init(&err); 11662306a36Sopenharmony_ci ret = parse_events(evlist, str, &err); 11762306a36Sopenharmony_ci if (err.str) 11862306a36Sopenharmony_ci parse_events_error__print(&err, "tracepoint"); 11962306a36Sopenharmony_ci parse_events_error__exit(&err); 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm, 12462306a36Sopenharmony_ci struct evlist *evlist) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci const char **events_ptr; 12762306a36Sopenharmony_ci int i, nr_tp = 0, err = -1; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Check for book3s_hv tracepoints */ 13062306a36Sopenharmony_ci for (events_ptr = ppc_book3s_hv_kvm_tp; *events_ptr; events_ptr++) { 13162306a36Sopenharmony_ci err = is_tracepoint_available(*events_ptr, evlist); 13262306a36Sopenharmony_ci if (err) 13362306a36Sopenharmony_ci return -1; 13462306a36Sopenharmony_ci nr_tp++; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < nr_tp; i++) 13862306a36Sopenharmony_ci kvm_events_tp[i] = ppc_book3s_hv_kvm_tp[i]; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kvm_events_tp[i] = NULL; 14162306a36Sopenharmony_ci kvm_exit_reason = "trap"; 14262306a36Sopenharmony_ci kvm->exit_reasons = hv_exit_reasons; 14362306a36Sopenharmony_ci kvm->exit_reasons_isa = "HV"; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* Wrapper to setup kvm tracepoints */ 14962306a36Sopenharmony_cistatic int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct evlist *evlist = evlist__new(); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (evlist == NULL) 15462306a36Sopenharmony_ci return -ENOMEM; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Right now, only supported on book3s_hv */ 15762306a36Sopenharmony_ci return ppc__setup_book3s_hv(kvm, evlist); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciint setup_kvm_events_tp(struct perf_kvm_stat *kvm) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return ppc__setup_kvm_tp(kvm); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciint cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci int ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = ppc__setup_kvm_tp(kvm); 17062306a36Sopenharmony_ci if (ret) { 17162306a36Sopenharmony_ci kvm->exit_reasons = NULL; 17262306a36Sopenharmony_ci kvm->exit_reasons_isa = NULL; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * In case of powerpc architecture, pmu registers are programmable 18062306a36Sopenharmony_ci * by guest kernel. So monitoring guest via host may not provide 18162306a36Sopenharmony_ci * valid samples with default 'cycles' event. It is better to use 18262306a36Sopenharmony_ci * 'trace_imc/trace_cycles' event for guest profiling, since it 18362306a36Sopenharmony_ci * can track the guest instruction pointer in the trace-record. 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * Function to parse the arguments and return appropriate values. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ciint kvm_add_default_arch_event(int *argc, const char **argv) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci const char **tmp; 19062306a36Sopenharmony_ci bool event = false; 19162306a36Sopenharmony_ci int i, j = *argc; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci const struct option event_options[] = { 19462306a36Sopenharmony_ci OPT_BOOLEAN('e', "event", &event, NULL), 19562306a36Sopenharmony_ci OPT_END() 19662306a36Sopenharmony_ci }; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci tmp = calloc(j + 1, sizeof(char *)); 19962306a36Sopenharmony_ci if (!tmp) 20062306a36Sopenharmony_ci return -EINVAL; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci for (i = 0; i < j; i++) 20362306a36Sopenharmony_ci tmp[i] = argv[i]; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN); 20662306a36Sopenharmony_ci if (!event) { 20762306a36Sopenharmony_ci if (perf_pmus__have_event("trace_imc", "trace_cycles")) { 20862306a36Sopenharmony_ci argv[j++] = strdup("-e"); 20962306a36Sopenharmony_ci argv[j++] = strdup("trace_imc/trace_cycles/"); 21062306a36Sopenharmony_ci *argc += 2; 21162306a36Sopenharmony_ci } else { 21262306a36Sopenharmony_ci free(tmp); 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci free(tmp); 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 220