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