Commit | Line | Data |
---|---|---|
220a0c60 CS |
1 | /* |
2 | * Hypervisor supplied "gpci" ("get performance counter info") performance | |
3 | * counter support | |
4 | * | |
5 | * Author: Cody P Schafer <cody@linux.vnet.ibm.com> | |
6 | * Copyright 2014 IBM Corporation. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #define pr_fmt(fmt) "hv-gpci: " fmt | |
15 | ||
16 | #include <linux/init.h> | |
17 | #include <linux/perf_event.h> | |
18 | #include <asm/firmware.h> | |
19 | #include <asm/hvcall.h> | |
20 | #include <asm/io.h> | |
21 | ||
22 | #include "hv-gpci.h" | |
23 | #include "hv-common.h" | |
24 | ||
25 | /* | |
26 | * Example usage: | |
27 | * perf stat -e 'hv_gpci/counter_info_version=3,offset=0,length=8, | |
28 | * secondary_index=0,starting_index=0xffffffff,request=0x10/' ... | |
29 | */ | |
30 | ||
31 | /* u32 */ | |
32 | EVENT_DEFINE_RANGE_FORMAT(request, config, 0, 31); | |
33 | /* u32 */ | |
9e9f6010 CS |
34 | /* |
35 | * Note that starting_index, phys_processor_idx, sibling_part_id, | |
36 | * hw_chip_id, partition_id all refer to the same bit range. They | |
37 | * are basically aliases for the starting_index. The specific alias | |
38 | * used depends on the event. See REQUEST_IDX_KIND in hv-gpci-requests.h | |
39 | */ | |
220a0c60 | 40 | EVENT_DEFINE_RANGE_FORMAT(starting_index, config, 32, 63); |
9e9f6010 CS |
41 | EVENT_DEFINE_RANGE_FORMAT_LITE(phys_processor_idx, config, 32, 63); |
42 | EVENT_DEFINE_RANGE_FORMAT_LITE(sibling_part_id, config, 32, 63); | |
43 | EVENT_DEFINE_RANGE_FORMAT_LITE(hw_chip_id, config, 32, 63); | |
44 | EVENT_DEFINE_RANGE_FORMAT_LITE(partition_id, config, 32, 63); | |
45 | ||
220a0c60 CS |
46 | /* u16 */ |
47 | EVENT_DEFINE_RANGE_FORMAT(secondary_index, config1, 0, 15); | |
48 | /* u8 */ | |
49 | EVENT_DEFINE_RANGE_FORMAT(counter_info_version, config1, 16, 23); | |
50 | /* u8, bytes of data (1-8) */ | |
51 | EVENT_DEFINE_RANGE_FORMAT(length, config1, 24, 31); | |
52 | /* u32, byte offset */ | |
53 | EVENT_DEFINE_RANGE_FORMAT(offset, config1, 32, 63); | |
54 | ||
55 | static struct attribute *format_attrs[] = { | |
56 | &format_attr_request.attr, | |
57 | &format_attr_starting_index.attr, | |
9e9f6010 CS |
58 | &format_attr_phys_processor_idx.attr, |
59 | &format_attr_sibling_part_id.attr, | |
60 | &format_attr_hw_chip_id.attr, | |
61 | &format_attr_partition_id.attr, | |
220a0c60 CS |
62 | &format_attr_secondary_index.attr, |
63 | &format_attr_counter_info_version.attr, | |
64 | ||
65 | &format_attr_offset.attr, | |
66 | &format_attr_length.attr, | |
67 | NULL, | |
68 | }; | |
69 | ||
70 | static struct attribute_group format_group = { | |
71 | .name = "format", | |
72 | .attrs = format_attrs, | |
73 | }; | |
74 | ||
9e9f6010 CS |
75 | static struct attribute_group event_group = { |
76 | .name = "events", | |
77 | .attrs = hv_gpci_event_attrs, | |
78 | }; | |
79 | ||
220a0c60 CS |
80 | #define HV_CAPS_ATTR(_name, _format) \ |
81 | static ssize_t _name##_show(struct device *dev, \ | |
82 | struct device_attribute *attr, \ | |
83 | char *page) \ | |
84 | { \ | |
85 | struct hv_perf_caps caps; \ | |
86 | unsigned long hret = hv_perf_caps_get(&caps); \ | |
87 | if (hret) \ | |
88 | return -EIO; \ | |
89 | \ | |
90 | return sprintf(page, _format, caps._name); \ | |
91 | } \ | |
92 | static struct device_attribute hv_caps_attr_##_name = __ATTR_RO(_name) | |
93 | ||
94 | static ssize_t kernel_version_show(struct device *dev, | |
95 | struct device_attribute *attr, | |
96 | char *page) | |
97 | { | |
98 | return sprintf(page, "0x%x\n", COUNTER_INFO_VERSION_CURRENT); | |
99 | } | |
100 | ||
58a685c2 | 101 | static DEVICE_ATTR_RO(kernel_version); |
220a0c60 CS |
102 | HV_CAPS_ATTR(version, "0x%x\n"); |
103 | HV_CAPS_ATTR(ga, "%d\n"); | |
104 | HV_CAPS_ATTR(expanded, "%d\n"); | |
105 | HV_CAPS_ATTR(lab, "%d\n"); | |
106 | HV_CAPS_ATTR(collect_privileged, "%d\n"); | |
107 | ||
108 | static struct attribute *interface_attrs[] = { | |
109 | &dev_attr_kernel_version.attr, | |
110 | &hv_caps_attr_version.attr, | |
111 | &hv_caps_attr_ga.attr, | |
112 | &hv_caps_attr_expanded.attr, | |
113 | &hv_caps_attr_lab.attr, | |
114 | &hv_caps_attr_collect_privileged.attr, | |
115 | NULL, | |
116 | }; | |
117 | ||
118 | static struct attribute_group interface_group = { | |
119 | .name = "interface", | |
120 | .attrs = interface_attrs, | |
121 | }; | |
122 | ||
123 | static const struct attribute_group *attr_groups[] = { | |
124 | &format_group, | |
9e9f6010 | 125 | &event_group, |
220a0c60 CS |
126 | &interface_group, |
127 | NULL, | |
128 | }; | |
129 | ||
e4f226b1 SB |
130 | #define HGPCI_REQ_BUFFER_SIZE 4096 |
131 | #define HGPCI_MAX_DATA_BYTES \ | |
132 | (HGPCI_REQ_BUFFER_SIZE - sizeof(struct hv_get_perf_counter_info_params)) | |
133 | ||
134 | DEFINE_PER_CPU(char, hv_gpci_reqb[HGPCI_REQ_BUFFER_SIZE]) __aligned(sizeof(uint64_t)); | |
135 | ||
136 | struct hv_gpci_request_buffer { | |
137 | struct hv_get_perf_counter_info_params params; | |
138 | uint8_t bytes[HGPCI_MAX_DATA_BYTES]; | |
139 | } __packed; | |
220a0c60 CS |
140 | |
141 | static unsigned long single_gpci_request(u32 req, u32 starting_index, | |
142 | u16 secondary_index, u8 version_in, u32 offset, u8 length, | |
143 | u64 *value) | |
144 | { | |
145 | unsigned long ret; | |
146 | size_t i; | |
147 | u64 count; | |
e4f226b1 SB |
148 | struct hv_gpci_request_buffer *arg; |
149 | ||
150 | arg = (void *)get_cpu_var(hv_gpci_reqb); | |
151 | memset(arg, 0, HGPCI_REQ_BUFFER_SIZE); | |
220a0c60 | 152 | |
e4f226b1 SB |
153 | arg->params.counter_request = cpu_to_be32(req); |
154 | arg->params.starting_index = cpu_to_be32(starting_index); | |
155 | arg->params.secondary_index = cpu_to_be16(secondary_index); | |
156 | arg->params.counter_info_version_in = version_in; | |
220a0c60 CS |
157 | |
158 | ret = plpar_hcall_norets(H_GET_PERF_COUNTER_INFO, | |
e4f226b1 | 159 | virt_to_phys(arg), HGPCI_REQ_BUFFER_SIZE); |
220a0c60 CS |
160 | if (ret) { |
161 | pr_devel("hcall failed: 0x%lx\n", ret); | |
e4f226b1 | 162 | goto out; |
220a0c60 CS |
163 | } |
164 | ||
165 | /* | |
166 | * we verify offset and length are within the zeroed buffer at event | |
167 | * init. | |
168 | */ | |
169 | count = 0; | |
170 | for (i = offset; i < offset + length; i++) | |
e4f226b1 | 171 | count |= arg->bytes[i] << (i - offset); |
220a0c60 CS |
172 | |
173 | *value = count; | |
e4f226b1 SB |
174 | out: |
175 | put_cpu_var(hv_gpci_reqb); | |
220a0c60 CS |
176 | return ret; |
177 | } | |
178 | ||
179 | static u64 h_gpci_get_value(struct perf_event *event) | |
180 | { | |
181 | u64 count; | |
182 | unsigned long ret = single_gpci_request(event_get_request(event), | |
183 | event_get_starting_index(event), | |
184 | event_get_secondary_index(event), | |
185 | event_get_counter_info_version(event), | |
186 | event_get_offset(event), | |
187 | event_get_length(event), | |
188 | &count); | |
189 | if (ret) | |
190 | return 0; | |
191 | return count; | |
192 | } | |
193 | ||
194 | static void h_gpci_event_update(struct perf_event *event) | |
195 | { | |
196 | s64 prev; | |
197 | u64 now = h_gpci_get_value(event); | |
198 | prev = local64_xchg(&event->hw.prev_count, now); | |
199 | local64_add(now - prev, &event->count); | |
200 | } | |
201 | ||
202 | static void h_gpci_event_start(struct perf_event *event, int flags) | |
203 | { | |
204 | local64_set(&event->hw.prev_count, h_gpci_get_value(event)); | |
205 | } | |
206 | ||
207 | static void h_gpci_event_stop(struct perf_event *event, int flags) | |
208 | { | |
209 | h_gpci_event_update(event); | |
210 | } | |
211 | ||
212 | static int h_gpci_event_add(struct perf_event *event, int flags) | |
213 | { | |
214 | if (flags & PERF_EF_START) | |
215 | h_gpci_event_start(event, flags); | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static int h_gpci_event_init(struct perf_event *event) | |
221 | { | |
222 | u64 count; | |
223 | u8 length; | |
224 | ||
225 | /* Not our event */ | |
226 | if (event->attr.type != event->pmu->type) | |
227 | return -ENOENT; | |
228 | ||
229 | /* config2 is unused */ | |
230 | if (event->attr.config2) { | |
231 | pr_devel("config2 set when reserved\n"); | |
232 | return -EINVAL; | |
233 | } | |
234 | ||
235 | /* unsupported modes and filters */ | |
236 | if (event->attr.exclude_user || | |
237 | event->attr.exclude_kernel || | |
238 | event->attr.exclude_hv || | |
239 | event->attr.exclude_idle || | |
240 | event->attr.exclude_host || | |
cc56d673 | 241 | event->attr.exclude_guest) |
220a0c60 CS |
242 | return -EINVAL; |
243 | ||
244 | /* no branch sampling */ | |
245 | if (has_branch_stack(event)) | |
246 | return -EOPNOTSUPP; | |
247 | ||
248 | length = event_get_length(event); | |
249 | if (length < 1 || length > 8) { | |
250 | pr_devel("length invalid\n"); | |
251 | return -EINVAL; | |
252 | } | |
253 | ||
254 | /* last byte within the buffer? */ | |
e4f226b1 | 255 | if ((event_get_offset(event) + length) > HGPCI_MAX_DATA_BYTES) { |
220a0c60 CS |
256 | pr_devel("request outside of buffer: %zu > %zu\n", |
257 | (size_t)event_get_offset(event) + length, | |
e4f226b1 | 258 | HGPCI_MAX_DATA_BYTES); |
220a0c60 CS |
259 | return -EINVAL; |
260 | } | |
261 | ||
262 | /* check if the request works... */ | |
263 | if (single_gpci_request(event_get_request(event), | |
264 | event_get_starting_index(event), | |
265 | event_get_secondary_index(event), | |
266 | event_get_counter_info_version(event), | |
267 | event_get_offset(event), | |
268 | length, | |
269 | &count)) { | |
270 | pr_devel("gpci hcall failed\n"); | |
271 | return -EINVAL; | |
272 | } | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
220a0c60 CS |
277 | static struct pmu h_gpci_pmu = { |
278 | .task_ctx_nr = perf_invalid_context, | |
279 | ||
280 | .name = "hv_gpci", | |
281 | .attr_groups = attr_groups, | |
282 | .event_init = h_gpci_event_init, | |
283 | .add = h_gpci_event_add, | |
284 | .del = h_gpci_event_stop, | |
285 | .start = h_gpci_event_start, | |
286 | .stop = h_gpci_event_stop, | |
287 | .read = h_gpci_event_update, | |
220a0c60 CS |
288 | }; |
289 | ||
290 | static int hv_gpci_init(void) | |
291 | { | |
292 | int r; | |
293 | unsigned long hret; | |
294 | struct hv_perf_caps caps; | |
295 | ||
9e9f6010 CS |
296 | hv_gpci_assert_offsets_correct(); |
297 | ||
220a0c60 | 298 | if (!firmware_has_feature(FW_FEATURE_LPAR)) { |
0a8cf9e2 | 299 | pr_debug("not a virtualized system, not enabling\n"); |
220a0c60 CS |
300 | return -ENODEV; |
301 | } | |
302 | ||
303 | hret = hv_perf_caps_get(&caps); | |
304 | if (hret) { | |
0a8cf9e2 | 305 | pr_debug("could not obtain capabilities, not enabling, rc=%ld\n", |
220a0c60 CS |
306 | hret); |
307 | return -ENODEV; | |
308 | } | |
309 | ||
cc56d673 VW |
310 | /* sampling not supported */ |
311 | h_gpci_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; | |
312 | ||
220a0c60 CS |
313 | r = perf_pmu_register(&h_gpci_pmu, h_gpci_pmu.name, -1); |
314 | if (r) | |
315 | return r; | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | device_initcall(hv_gpci_init); |