xref: /linux/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include "../kvm-stat.h"
4 #include "../parse-events.h"
5 #include "../debug.h"
6 #include "../evsel.h"
7 #include "../evlist.h"
8 #include "../pmus.h"
9 
10 #include "book3s_hv_exits.h"
11 #include "book3s_hcalls.h"
12 
13 #define NR_TPS 4
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 static const char * const 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 static const char *__kvm_events_tp[NR_TPS + 1];
29 
30 static void hcall_event_get_key(struct perf_sample *sample,
31 				struct event_key *key)
32 {
33 	key->info = 0;
34 	key->key = perf_sample__intval(sample, "req");
35 }
36 
37 static const char *get_hcall_exit_reason(u64 exit_code)
38 {
39 	struct exit_reasons_table *tbl = hcall_reasons;
40 
41 	while (tbl->reason != NULL) {
42 		if (tbl->exit_code == exit_code)
43 			return tbl->reason;
44 		tbl++;
45 	}
46 
47 	pr_debug("Unknown hcall code: %lld\n",
48 	       (unsigned long long)exit_code);
49 	return "UNKNOWN";
50 }
51 
52 static bool hcall_event_end(struct perf_sample *sample,
53 			    struct event_key *key __maybe_unused)
54 {
55 	return evsel__name_is(sample->evsel, __kvm_events_tp[3]);
56 }
57 
58 static bool hcall_event_begin(struct perf_sample *sample, struct event_key *key)
59 {
60 	if (evsel__name_is(sample->evsel, __kvm_events_tp[2])) {
61 		hcall_event_get_key(sample, key);
62 		return true;
63 	}
64 
65 	return false;
66 }
67 static void hcall_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
68 				   struct event_key *key,
69 				   char *decode)
70 {
71 	const char *hcall_reason = get_hcall_exit_reason(key->key);
72 
73 	scnprintf(decode, KVM_EVENT_NAME_LEN, "%s", hcall_reason);
74 }
75 
76 static const struct kvm_events_ops hcall_events = {
77 	.is_begin_event = hcall_event_begin,
78 	.is_end_event = hcall_event_end,
79 	.decode_key = hcall_event_decode_key,
80 	.name = "HCALL-EVENT",
81 };
82 
83 static const struct kvm_events_ops exit_events = {
84 	.is_begin_event = exit_event_begin,
85 	.is_end_event = exit_event_end,
86 	.decode_key = exit_event_decode_key,
87 	.name = "VM-EXIT"
88 };
89 
90 static const struct kvm_reg_events_ops __kvm_reg_events_ops[] = {
91 	{ .name = "vmexit", .ops = &exit_events },
92 	{ .name = "hcall", .ops = &hcall_events },
93 	{ NULL, NULL },
94 };
95 
96 static const char * const __kvm_skip_events[] = {
97 	NULL,
98 };
99 
100 
101 static int is_tracepoint_available(const char *str, struct evlist *evlist)
102 {
103 	struct parse_events_error err;
104 	int ret;
105 
106 	parse_events_error__init(&err);
107 	ret = parse_events(evlist, str, &err);
108 	if (ret)
109 		parse_events_error__print(&err, "tracepoint");
110 	parse_events_error__exit(&err);
111 	return ret;
112 }
113 
114 static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm,
115 				struct evlist *evlist)
116 {
117 	const char * const *events_ptr;
118 	int i, nr_tp = 0, err = -1;
119 
120 	/* Check for book3s_hv tracepoints */
121 	for (events_ptr = ppc_book3s_hv_kvm_tp; *events_ptr; events_ptr++) {
122 		err = is_tracepoint_available(*events_ptr, evlist);
123 		if (err)
124 			return -1;
125 		nr_tp++;
126 	}
127 
128 	for (i = 0; i < nr_tp; i++)
129 		__kvm_events_tp[i] = ppc_book3s_hv_kvm_tp[i];
130 
131 	__kvm_events_tp[i] = NULL;
132 	kvm->exit_reasons = hv_exit_reasons;
133 	kvm->exit_reasons_isa = "HV";
134 
135 	return 0;
136 }
137 
138 /* Wrapper to setup kvm tracepoints */
139 static int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm)
140 {
141 	struct evlist *evlist = evlist__new();
142 
143 	if (evlist == NULL)
144 		return -ENOMEM;
145 
146 	/* Right now, only supported on book3s_hv */
147 	return ppc__setup_book3s_hv(kvm, evlist);
148 }
149 
150 int __setup_kvm_events_tp_powerpc(struct perf_kvm_stat *kvm)
151 {
152 	return ppc__setup_kvm_tp(kvm);
153 }
154 
155 int __cpu_isa_init_powerpc(struct perf_kvm_stat *kvm)
156 {
157 	int ret;
158 
159 	ret = ppc__setup_kvm_tp(kvm);
160 	if (ret) {
161 		kvm->exit_reasons = NULL;
162 		kvm->exit_reasons_isa = NULL;
163 	}
164 
165 	return ret;
166 }
167 
168 /*
169  * In case of powerpc architecture, pmu registers are programmable
170  * by guest kernel. So monitoring guest via host may not provide
171  * valid samples with default 'cycles' event. It is better to use
172  * 'trace_imc/trace_cycles' event for guest profiling, since it
173  * can track the guest instruction pointer in the trace-record.
174  *
175  * Function to parse the arguments and return appropriate values.
176  */
177 int __kvm_add_default_arch_event_powerpc(int *argc, const char **argv)
178 {
179 	int j = *argc;
180 
181 	if (!perf_pmus__have_event("trace_imc", "trace_cycles"))
182 		return -EINVAL;
183 
184 	argv[j++] = "-e";
185 	argv[j++] = "trace_imc/trace_cycles/";
186 	*argc += 2;
187 
188 	return 0;
189 }
190 
191 const char * const *__kvm_events_tp_powerpc(void)
192 {
193 	return __kvm_events_tp;
194 }
195 
196 const struct kvm_reg_events_ops *__kvm_reg_events_ops_powerpc(void)
197 {
198 	return __kvm_reg_events_ops;
199 }
200 
201 const char * const *__kvm_skip_events_powerpc(void)
202 {
203 	return __kvm_skip_events;
204 }
205