GNU Linux-libre 4.9.328-gnu1
[releases.git] / tools / perf / arch / powerpc / util / kvm-stat.c
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 }