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