1 | /* |
2 | * Copyright 2019 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | */ |
23 | |
24 | #include <linux/perf_event.h> |
25 | #include <linux/init.h> |
26 | #include "amdgpu.h" |
27 | #include "amdgpu_pmu.h" |
28 | |
29 | #define PMU_NAME_SIZE 32 |
30 | #define NUM_FORMATS_AMDGPU_PMU 4 |
31 | #define NUM_FORMATS_DF_VEGA20 3 |
32 | #define NUM_EVENTS_DF_VEGA20 8 |
33 | #define NUM_EVENT_TYPES_VEGA20 1 |
34 | #define NUM_EVENTS_VEGA20_XGMI 2 |
35 | #define NUM_EVENTS_VEGA20_MAX NUM_EVENTS_VEGA20_XGMI |
36 | #define NUM_EVENT_TYPES_ARCTURUS 1 |
37 | #define NUM_EVENTS_ARCTURUS_XGMI 6 |
38 | #define NUM_EVENTS_ARCTURUS_MAX NUM_EVENTS_ARCTURUS_XGMI |
39 | |
40 | struct amdgpu_pmu_event_attribute { |
41 | struct device_attribute attr; |
42 | const char *event_str; |
43 | unsigned int type; |
44 | }; |
45 | |
46 | /* record to keep track of pmu entry per pmu type per device */ |
47 | struct amdgpu_pmu_entry { |
48 | struct list_head entry; |
49 | struct amdgpu_device *adev; |
50 | struct pmu pmu; |
51 | unsigned int pmu_perf_type; |
52 | char *pmu_type_name; |
53 | char *pmu_file_prefix; |
54 | struct attribute_group fmt_attr_group; |
55 | struct amdgpu_pmu_event_attribute *fmt_attr; |
56 | struct attribute_group evt_attr_group; |
57 | struct amdgpu_pmu_event_attribute *evt_attr; |
58 | }; |
59 | |
60 | static ssize_t amdgpu_pmu_event_show(struct device *dev, |
61 | struct device_attribute *attr, char *buf) |
62 | { |
63 | struct amdgpu_pmu_event_attribute *amdgpu_pmu_attr; |
64 | |
65 | amdgpu_pmu_attr = container_of(attr, struct amdgpu_pmu_event_attribute, |
66 | attr); |
67 | |
68 | if (!amdgpu_pmu_attr->type) |
69 | return sprintf(buf, fmt: "%s\n" , amdgpu_pmu_attr->event_str); |
70 | |
71 | return sprintf(buf, fmt: "%s,type=0x%x\n" , |
72 | amdgpu_pmu_attr->event_str, amdgpu_pmu_attr->type); |
73 | } |
74 | |
75 | static LIST_HEAD(amdgpu_pmu_list); |
76 | |
77 | |
78 | struct amdgpu_pmu_attr { |
79 | const char *name; |
80 | const char *config; |
81 | }; |
82 | |
83 | struct amdgpu_pmu_type { |
84 | const unsigned int type; |
85 | const unsigned int num_of_type; |
86 | }; |
87 | |
88 | struct amdgpu_pmu_config { |
89 | struct amdgpu_pmu_attr *formats; |
90 | unsigned int num_formats; |
91 | struct amdgpu_pmu_attr *events; |
92 | unsigned int num_events; |
93 | struct amdgpu_pmu_type *types; |
94 | unsigned int num_types; |
95 | }; |
96 | |
97 | /* |
98 | * Events fall under two categories: |
99 | * - PMU typed |
100 | * Events in /sys/bus/event_source/devices/amdgpu_<pmu_type>_<dev_num> have |
101 | * performance counter operations handled by one IP <pmu_type>. Formats and |
102 | * events should be defined by <pmu_type>_<asic_type>_formats and |
103 | * <pmu_type>_<asic_type>_events respectively. |
104 | * |
105 | * - Event config typed |
106 | * Events in /sys/bus/event_source/devices/amdgpu_<dev_num> have performance |
107 | * counter operations that can be handled by multiple IPs dictated by their |
108 | * "type" format field. Formats and events should be defined by |
109 | * amdgpu_pmu_formats and <asic_type>_events respectively. Format field |
110 | * "type" is generated in amdgpu_pmu_event_show and defined in |
111 | * <asic_type>_event_config_types. |
112 | */ |
113 | |
114 | static struct amdgpu_pmu_attr amdgpu_pmu_formats[NUM_FORMATS_AMDGPU_PMU] = { |
115 | { .name = "event" , .config = "config:0-7" }, |
116 | { .name = "instance" , .config = "config:8-15" }, |
117 | { .name = "umask" , .config = "config:16-23" }, |
118 | { .name = "type" , .config = "config:56-63" } |
119 | }; |
120 | |
121 | /* Vega20 events */ |
122 | static struct amdgpu_pmu_attr vega20_events[NUM_EVENTS_VEGA20_MAX] = { |
123 | { .name = "xgmi_link0_data_outbound" , |
124 | .config = "event=0x7,instance=0x46,umask=0x2" }, |
125 | { .name = "xgmi_link1_data_outbound" , |
126 | .config = "event=0x7,instance=0x47,umask=0x2" } |
127 | }; |
128 | |
129 | static struct amdgpu_pmu_type vega20_types[NUM_EVENT_TYPES_VEGA20] = { |
130 | { .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI, |
131 | .num_of_type = NUM_EVENTS_VEGA20_XGMI } |
132 | }; |
133 | |
134 | static struct amdgpu_pmu_config vega20_config = { |
135 | .formats = amdgpu_pmu_formats, |
136 | .num_formats = ARRAY_SIZE(amdgpu_pmu_formats), |
137 | .events = vega20_events, |
138 | .num_events = ARRAY_SIZE(vega20_events), |
139 | .types = vega20_types, |
140 | .num_types = ARRAY_SIZE(vega20_types) |
141 | }; |
142 | |
143 | /* Vega20 data fabric (DF) events */ |
144 | static struct amdgpu_pmu_attr df_vega20_formats[NUM_FORMATS_DF_VEGA20] = { |
145 | { .name = "event" , .config = "config:0-7" }, |
146 | { .name = "instance" , .config = "config:8-15" }, |
147 | { .name = "umask" , .config = "config:16-23" } |
148 | }; |
149 | |
150 | static struct amdgpu_pmu_attr df_vega20_events[NUM_EVENTS_DF_VEGA20] = { |
151 | { .name = "cake0_pcsout_txdata" , |
152 | .config = "event=0x7,instance=0x46,umask=0x2" }, |
153 | { .name = "cake1_pcsout_txdata" , |
154 | .config = "event=0x7,instance=0x47,umask=0x2" }, |
155 | { .name = "cake0_pcsout_txmeta" , |
156 | .config = "event=0x7,instance=0x46,umask=0x4" }, |
157 | { .name = "cake1_pcsout_txmeta" , |
158 | .config = "event=0x7,instance=0x47,umask=0x4" }, |
159 | { .name = "cake0_ftiinstat_reqalloc" , |
160 | .config = "event=0xb,instance=0x46,umask=0x4" }, |
161 | { .name = "cake1_ftiinstat_reqalloc" , |
162 | .config = "event=0xb,instance=0x47,umask=0x4" }, |
163 | { .name = "cake0_ftiinstat_rspalloc" , |
164 | .config = "event=0xb,instance=0x46,umask=0x8" }, |
165 | { .name = "cake1_ftiinstat_rspalloc" , |
166 | .config = "event=0xb,instance=0x47,umask=0x8" } |
167 | }; |
168 | |
169 | static struct amdgpu_pmu_config df_vega20_config = { |
170 | .formats = df_vega20_formats, |
171 | .num_formats = ARRAY_SIZE(df_vega20_formats), |
172 | .events = df_vega20_events, |
173 | .num_events = ARRAY_SIZE(df_vega20_events), |
174 | .types = NULL, |
175 | .num_types = 0 |
176 | }; |
177 | |
178 | /* Arcturus events */ |
179 | static struct amdgpu_pmu_attr arcturus_events[NUM_EVENTS_ARCTURUS_MAX] = { |
180 | { .name = "xgmi_link0_data_outbound" , |
181 | .config = "event=0x7,instance=0x4b,umask=0x2" }, |
182 | { .name = "xgmi_link1_data_outbound" , |
183 | .config = "event=0x7,instance=0x4c,umask=0x2" }, |
184 | { .name = "xgmi_link2_data_outbound" , |
185 | .config = "event=0x7,instance=0x4d,umask=0x2" }, |
186 | { .name = "xgmi_link3_data_outbound" , |
187 | .config = "event=0x7,instance=0x4e,umask=0x2" }, |
188 | { .name = "xgmi_link4_data_outbound" , |
189 | .config = "event=0x7,instance=0x4f,umask=0x2" }, |
190 | { .name = "xgmi_link5_data_outbound" , |
191 | .config = "event=0x7,instance=0x50,umask=0x2" } |
192 | }; |
193 | |
194 | static struct amdgpu_pmu_type arcturus_types[NUM_EVENT_TYPES_ARCTURUS] = { |
195 | { .type = AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI, |
196 | .num_of_type = NUM_EVENTS_ARCTURUS_XGMI } |
197 | }; |
198 | |
199 | static struct amdgpu_pmu_config arcturus_config = { |
200 | .formats = amdgpu_pmu_formats, |
201 | .num_formats = ARRAY_SIZE(amdgpu_pmu_formats), |
202 | .events = arcturus_events, |
203 | .num_events = ARRAY_SIZE(arcturus_events), |
204 | .types = arcturus_types, |
205 | .num_types = ARRAY_SIZE(arcturus_types) |
206 | }; |
207 | |
208 | /* initialize perf counter */ |
209 | static int amdgpu_perf_event_init(struct perf_event *event) |
210 | { |
211 | struct hw_perf_event *hwc = &event->hw; |
212 | |
213 | /* test the event attr type check for PMU enumeration */ |
214 | if (event->attr.type != event->pmu->type) |
215 | return -ENOENT; |
216 | |
217 | /* update the hw_perf_event struct with config data */ |
218 | hwc->config = event->attr.config; |
219 | hwc->config_base = AMDGPU_PMU_PERF_TYPE_NONE; |
220 | |
221 | return 0; |
222 | } |
223 | |
224 | /* start perf counter */ |
225 | static void amdgpu_perf_start(struct perf_event *event, int flags) |
226 | { |
227 | struct hw_perf_event *hwc = &event->hw; |
228 | struct amdgpu_pmu_entry *pe = container_of(event->pmu, |
229 | struct amdgpu_pmu_entry, |
230 | pmu); |
231 | int target_cntr = 0; |
232 | |
233 | if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) |
234 | return; |
235 | |
236 | if ((!pe->adev->df.funcs) || |
237 | (!pe->adev->df.funcs->pmc_start)) |
238 | return; |
239 | |
240 | WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); |
241 | hwc->state = 0; |
242 | |
243 | switch (hwc->config_base) { |
244 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: |
245 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: |
246 | if (!(flags & PERF_EF_RELOAD)) { |
247 | target_cntr = pe->adev->df.funcs->pmc_start(pe->adev, |
248 | hwc->config, 0 /* unused */, |
249 | 1 /* add counter */); |
250 | if (target_cntr < 0) |
251 | break; |
252 | |
253 | hwc->idx = target_cntr; |
254 | } |
255 | |
256 | pe->adev->df.funcs->pmc_start(pe->adev, hwc->config, |
257 | hwc->idx, 0); |
258 | break; |
259 | default: |
260 | break; |
261 | } |
262 | |
263 | perf_event_update_userpage(event); |
264 | } |
265 | |
266 | /* read perf counter */ |
267 | static void amdgpu_perf_read(struct perf_event *event) |
268 | { |
269 | struct hw_perf_event *hwc = &event->hw; |
270 | struct amdgpu_pmu_entry *pe = container_of(event->pmu, |
271 | struct amdgpu_pmu_entry, |
272 | pmu); |
273 | u64 count, prev; |
274 | |
275 | if ((!pe->adev->df.funcs) || |
276 | (!pe->adev->df.funcs->pmc_get_count)) |
277 | return; |
278 | |
279 | prev = local64_read(&hwc->prev_count); |
280 | do { |
281 | switch (hwc->config_base) { |
282 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: |
283 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: |
284 | pe->adev->df.funcs->pmc_get_count(pe->adev, |
285 | hwc->config, hwc->idx, &count); |
286 | break; |
287 | default: |
288 | count = 0; |
289 | break; |
290 | } |
291 | } while (!local64_try_cmpxchg(l: &hwc->prev_count, old: &prev, new: count)); |
292 | |
293 | local64_add(count - prev, &event->count); |
294 | } |
295 | |
296 | /* stop perf counter */ |
297 | static void amdgpu_perf_stop(struct perf_event *event, int flags) |
298 | { |
299 | struct hw_perf_event *hwc = &event->hw; |
300 | struct amdgpu_pmu_entry *pe = container_of(event->pmu, |
301 | struct amdgpu_pmu_entry, |
302 | pmu); |
303 | |
304 | if (hwc->state & PERF_HES_UPTODATE) |
305 | return; |
306 | |
307 | if ((!pe->adev->df.funcs) || |
308 | (!pe->adev->df.funcs->pmc_stop)) |
309 | return; |
310 | |
311 | switch (hwc->config_base) { |
312 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: |
313 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: |
314 | pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx, |
315 | 0); |
316 | break; |
317 | default: |
318 | break; |
319 | } |
320 | |
321 | WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); |
322 | hwc->state |= PERF_HES_STOPPED; |
323 | |
324 | if (hwc->state & PERF_HES_UPTODATE) |
325 | return; |
326 | |
327 | amdgpu_perf_read(event); |
328 | hwc->state |= PERF_HES_UPTODATE; |
329 | } |
330 | |
331 | /* add perf counter */ |
332 | static int amdgpu_perf_add(struct perf_event *event, int flags) |
333 | { |
334 | struct hw_perf_event *hwc = &event->hw; |
335 | int retval = 0, target_cntr; |
336 | struct amdgpu_pmu_entry *pe = container_of(event->pmu, |
337 | struct amdgpu_pmu_entry, |
338 | pmu); |
339 | |
340 | if ((!pe->adev->df.funcs) || |
341 | (!pe->adev->df.funcs->pmc_start)) |
342 | return -EINVAL; |
343 | |
344 | switch (pe->pmu_perf_type) { |
345 | case AMDGPU_PMU_PERF_TYPE_DF: |
346 | hwc->config_base = AMDGPU_PMU_EVENT_CONFIG_TYPE_DF; |
347 | break; |
348 | case AMDGPU_PMU_PERF_TYPE_ALL: |
349 | hwc->config_base = (hwc->config >> |
350 | AMDGPU_PMU_EVENT_CONFIG_TYPE_SHIFT) & |
351 | AMDGPU_PMU_EVENT_CONFIG_TYPE_MASK; |
352 | break; |
353 | } |
354 | |
355 | event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; |
356 | |
357 | switch (hwc->config_base) { |
358 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: |
359 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: |
360 | target_cntr = pe->adev->df.funcs->pmc_start(pe->adev, |
361 | hwc->config, 0 /* unused */, |
362 | 1 /* add counter */); |
363 | if (target_cntr < 0) |
364 | retval = target_cntr; |
365 | else |
366 | hwc->idx = target_cntr; |
367 | |
368 | break; |
369 | default: |
370 | return 0; |
371 | } |
372 | |
373 | if (retval) |
374 | return retval; |
375 | |
376 | if (flags & PERF_EF_START) |
377 | amdgpu_perf_start(event, PERF_EF_RELOAD); |
378 | |
379 | return retval; |
380 | } |
381 | |
382 | /* delete perf counter */ |
383 | static void amdgpu_perf_del(struct perf_event *event, int flags) |
384 | { |
385 | struct hw_perf_event *hwc = &event->hw; |
386 | struct amdgpu_pmu_entry *pe = container_of(event->pmu, |
387 | struct amdgpu_pmu_entry, |
388 | pmu); |
389 | if ((!pe->adev->df.funcs) || |
390 | (!pe->adev->df.funcs->pmc_stop)) |
391 | return; |
392 | |
393 | amdgpu_perf_stop(event, PERF_EF_UPDATE); |
394 | |
395 | switch (hwc->config_base) { |
396 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_DF: |
397 | case AMDGPU_PMU_EVENT_CONFIG_TYPE_XGMI: |
398 | pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, hwc->idx, |
399 | 1); |
400 | break; |
401 | default: |
402 | break; |
403 | } |
404 | |
405 | perf_event_update_userpage(event); |
406 | } |
407 | |
408 | static void amdgpu_pmu_create_event_attrs_by_type( |
409 | struct attribute_group *attr_group, |
410 | struct amdgpu_pmu_event_attribute *pmu_attr, |
411 | struct amdgpu_pmu_attr events[], |
412 | int s_offset, |
413 | int e_offset, |
414 | unsigned int type) |
415 | { |
416 | int i; |
417 | |
418 | pmu_attr += s_offset; |
419 | |
420 | for (i = s_offset; i < e_offset; i++) { |
421 | attr_group->attrs[i] = &pmu_attr->attr.attr; |
422 | sysfs_attr_init(&pmu_attr->attr.attr); |
423 | pmu_attr->attr.attr.name = events[i].name; |
424 | pmu_attr->attr.attr.mode = 0444; |
425 | pmu_attr->attr.show = amdgpu_pmu_event_show; |
426 | pmu_attr->event_str = events[i].config; |
427 | pmu_attr->type = type; |
428 | pmu_attr++; |
429 | } |
430 | } |
431 | |
432 | static void amdgpu_pmu_create_attrs(struct attribute_group *attr_group, |
433 | struct amdgpu_pmu_event_attribute *pmu_attr, |
434 | struct amdgpu_pmu_attr events[], |
435 | int num_events) |
436 | { |
437 | amdgpu_pmu_create_event_attrs_by_type(attr_group, pmu_attr, events, s_offset: 0, |
438 | e_offset: num_events, type: AMDGPU_PMU_EVENT_CONFIG_TYPE_NONE); |
439 | } |
440 | |
441 | |
442 | static int amdgpu_pmu_alloc_pmu_attrs( |
443 | struct attribute_group *fmt_attr_group, |
444 | struct amdgpu_pmu_event_attribute **fmt_attr, |
445 | struct attribute_group *evt_attr_group, |
446 | struct amdgpu_pmu_event_attribute **evt_attr, |
447 | struct amdgpu_pmu_config *config) |
448 | { |
449 | *fmt_attr = kcalloc(n: config->num_formats, size: sizeof(**fmt_attr), |
450 | GFP_KERNEL); |
451 | |
452 | if (!(*fmt_attr)) |
453 | return -ENOMEM; |
454 | |
455 | fmt_attr_group->attrs = kcalloc(n: config->num_formats + 1, |
456 | size: sizeof(*fmt_attr_group->attrs), GFP_KERNEL); |
457 | |
458 | if (!fmt_attr_group->attrs) |
459 | goto err_fmt_attr_grp; |
460 | |
461 | *evt_attr = kcalloc(n: config->num_events, size: sizeof(**evt_attr), GFP_KERNEL); |
462 | |
463 | if (!(*evt_attr)) |
464 | goto err_evt_attr; |
465 | |
466 | evt_attr_group->attrs = kcalloc(n: config->num_events + 1, |
467 | size: sizeof(*evt_attr_group->attrs), GFP_KERNEL); |
468 | |
469 | if (!evt_attr_group->attrs) |
470 | goto err_evt_attr_grp; |
471 | |
472 | return 0; |
473 | err_evt_attr_grp: |
474 | kfree(objp: *evt_attr); |
475 | err_evt_attr: |
476 | kfree(objp: fmt_attr_group->attrs); |
477 | err_fmt_attr_grp: |
478 | kfree(objp: *fmt_attr); |
479 | return -ENOMEM; |
480 | } |
481 | |
482 | /* init pmu tracking per pmu type */ |
483 | static int init_pmu_entry_by_type_and_add(struct amdgpu_pmu_entry *pmu_entry, |
484 | struct amdgpu_pmu_config *config) |
485 | { |
486 | const struct attribute_group *attr_groups[] = { |
487 | &pmu_entry->fmt_attr_group, |
488 | &pmu_entry->evt_attr_group, |
489 | NULL |
490 | }; |
491 | char pmu_name[PMU_NAME_SIZE]; |
492 | int ret = 0, total_num_events = 0; |
493 | |
494 | pmu_entry->pmu = (struct pmu){ |
495 | .event_init = amdgpu_perf_event_init, |
496 | .add = amdgpu_perf_add, |
497 | .del = amdgpu_perf_del, |
498 | .start = amdgpu_perf_start, |
499 | .stop = amdgpu_perf_stop, |
500 | .read = amdgpu_perf_read, |
501 | .task_ctx_nr = perf_invalid_context, |
502 | }; |
503 | |
504 | ret = amdgpu_pmu_alloc_pmu_attrs(fmt_attr_group: &pmu_entry->fmt_attr_group, |
505 | fmt_attr: &pmu_entry->fmt_attr, |
506 | evt_attr_group: &pmu_entry->evt_attr_group, |
507 | evt_attr: &pmu_entry->evt_attr, |
508 | config); |
509 | |
510 | if (ret) |
511 | goto err_out; |
512 | |
513 | amdgpu_pmu_create_attrs(attr_group: &pmu_entry->fmt_attr_group, pmu_attr: pmu_entry->fmt_attr, |
514 | events: config->formats, num_events: config->num_formats); |
515 | |
516 | if (pmu_entry->pmu_perf_type == AMDGPU_PMU_PERF_TYPE_ALL) { |
517 | int i; |
518 | |
519 | for (i = 0; i < config->num_types; i++) { |
520 | amdgpu_pmu_create_event_attrs_by_type( |
521 | attr_group: &pmu_entry->evt_attr_group, |
522 | pmu_attr: pmu_entry->evt_attr, |
523 | events: config->events, |
524 | s_offset: total_num_events, |
525 | e_offset: total_num_events + |
526 | config->types[i].num_of_type, |
527 | type: config->types[i].type); |
528 | total_num_events += config->types[i].num_of_type; |
529 | } |
530 | } else { |
531 | amdgpu_pmu_create_attrs(attr_group: &pmu_entry->evt_attr_group, |
532 | pmu_attr: pmu_entry->evt_attr, |
533 | events: config->events, num_events: config->num_events); |
534 | total_num_events = config->num_events; |
535 | } |
536 | |
537 | pmu_entry->pmu.attr_groups = kmemdup(p: attr_groups, size: sizeof(attr_groups), |
538 | GFP_KERNEL); |
539 | |
540 | if (!pmu_entry->pmu.attr_groups) { |
541 | ret = -ENOMEM; |
542 | goto err_attr_group; |
543 | } |
544 | |
545 | snprintf(buf: pmu_name, PMU_NAME_SIZE, fmt: "%s_%d" , pmu_entry->pmu_file_prefix, |
546 | adev_to_drm(adev: pmu_entry->adev)->primary->index); |
547 | |
548 | ret = perf_pmu_register(pmu: &pmu_entry->pmu, name: pmu_name, type: -1); |
549 | |
550 | if (ret) |
551 | goto err_register; |
552 | |
553 | if (pmu_entry->pmu_perf_type != AMDGPU_PMU_PERF_TYPE_ALL) |
554 | pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n" , |
555 | pmu_entry->pmu_type_name, total_num_events); |
556 | else |
557 | pr_info("Detected AMDGPU %d Perf Events.\n" , total_num_events); |
558 | |
559 | |
560 | list_add_tail(new: &pmu_entry->entry, head: &amdgpu_pmu_list); |
561 | |
562 | return 0; |
563 | err_register: |
564 | kfree(objp: pmu_entry->pmu.attr_groups); |
565 | err_attr_group: |
566 | kfree(objp: pmu_entry->fmt_attr_group.attrs); |
567 | kfree(objp: pmu_entry->fmt_attr); |
568 | kfree(objp: pmu_entry->evt_attr_group.attrs); |
569 | kfree(objp: pmu_entry->evt_attr); |
570 | err_out: |
571 | pr_warn("Error initializing AMDGPU %s PMUs.\n" , |
572 | pmu_entry->pmu_type_name); |
573 | return ret; |
574 | } |
575 | |
576 | /* destroy all pmu data associated with target device */ |
577 | void amdgpu_pmu_fini(struct amdgpu_device *adev) |
578 | { |
579 | struct amdgpu_pmu_entry *pe, *temp; |
580 | |
581 | list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) { |
582 | if (pe->adev != adev) |
583 | continue; |
584 | list_del(entry: &pe->entry); |
585 | perf_pmu_unregister(pmu: &pe->pmu); |
586 | kfree(objp: pe->pmu.attr_groups); |
587 | kfree(objp: pe->fmt_attr_group.attrs); |
588 | kfree(objp: pe->fmt_attr); |
589 | kfree(objp: pe->evt_attr_group.attrs); |
590 | kfree(objp: pe->evt_attr); |
591 | kfree(objp: pe); |
592 | } |
593 | } |
594 | |
595 | static struct amdgpu_pmu_entry *create_pmu_entry(struct amdgpu_device *adev, |
596 | unsigned int pmu_type, |
597 | char *pmu_type_name, |
598 | char *pmu_file_prefix) |
599 | { |
600 | struct amdgpu_pmu_entry *pmu_entry; |
601 | |
602 | pmu_entry = kzalloc(size: sizeof(struct amdgpu_pmu_entry), GFP_KERNEL); |
603 | |
604 | if (!pmu_entry) |
605 | return pmu_entry; |
606 | |
607 | pmu_entry->adev = adev; |
608 | pmu_entry->fmt_attr_group.name = "format" ; |
609 | pmu_entry->fmt_attr_group.attrs = NULL; |
610 | pmu_entry->evt_attr_group.name = "events" ; |
611 | pmu_entry->evt_attr_group.attrs = NULL; |
612 | pmu_entry->pmu_perf_type = pmu_type; |
613 | pmu_entry->pmu_type_name = pmu_type_name; |
614 | pmu_entry->pmu_file_prefix = pmu_file_prefix; |
615 | |
616 | return pmu_entry; |
617 | } |
618 | |
619 | /* init amdgpu_pmu */ |
620 | int amdgpu_pmu_init(struct amdgpu_device *adev) |
621 | { |
622 | int ret = 0; |
623 | struct amdgpu_pmu_entry *pmu_entry, *pmu_entry_df; |
624 | |
625 | switch (adev->asic_type) { |
626 | case CHIP_VEGA20: |
627 | pmu_entry_df = create_pmu_entry(adev, pmu_type: AMDGPU_PMU_PERF_TYPE_DF, |
628 | pmu_type_name: "DF" , pmu_file_prefix: "amdgpu_df" ); |
629 | |
630 | if (!pmu_entry_df) |
631 | return -ENOMEM; |
632 | |
633 | ret = init_pmu_entry_by_type_and_add(pmu_entry: pmu_entry_df, |
634 | config: &df_vega20_config); |
635 | |
636 | if (ret) { |
637 | kfree(objp: pmu_entry_df); |
638 | return ret; |
639 | } |
640 | |
641 | pmu_entry = create_pmu_entry(adev, pmu_type: AMDGPU_PMU_PERF_TYPE_ALL, |
642 | pmu_type_name: "" , pmu_file_prefix: "amdgpu" ); |
643 | |
644 | if (!pmu_entry) { |
645 | amdgpu_pmu_fini(adev); |
646 | return -ENOMEM; |
647 | } |
648 | |
649 | ret = init_pmu_entry_by_type_and_add(pmu_entry, |
650 | config: &vega20_config); |
651 | |
652 | if (ret) { |
653 | kfree(objp: pmu_entry); |
654 | amdgpu_pmu_fini(adev); |
655 | return ret; |
656 | } |
657 | |
658 | break; |
659 | case CHIP_ARCTURUS: |
660 | pmu_entry = create_pmu_entry(adev, pmu_type: AMDGPU_PMU_PERF_TYPE_ALL, |
661 | pmu_type_name: "" , pmu_file_prefix: "amdgpu" ); |
662 | if (!pmu_entry) |
663 | return -ENOMEM; |
664 | |
665 | ret = init_pmu_entry_by_type_and_add(pmu_entry, |
666 | config: &arcturus_config); |
667 | |
668 | if (ret) { |
669 | kfree(objp: pmu_entry); |
670 | return -ENOMEM; |
671 | } |
672 | |
673 | break; |
674 | |
675 | default: |
676 | return 0; |
677 | } |
678 | |
679 | return ret; |
680 | } |
681 | |