2 * Copyright 2019 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
22 * Author: Jonathan Kim <jonathan.kim@amd.com>
26 #include <linux/perf_event.h>
27 #include <linux/init.h>
29 #include "amdgpu_pmu.h"
32 #define PMU_NAME_SIZE 32
34 /* record to keep track of pmu entry per pmu type per device */
35 struct amdgpu_pmu_entry
{
36 struct list_head entry
;
37 struct amdgpu_device
*adev
;
39 unsigned int pmu_perf_type
;
42 static LIST_HEAD(amdgpu_pmu_list
);
45 /* initialize perf counter */
46 static int amdgpu_perf_event_init(struct perf_event
*event
)
48 struct hw_perf_event
*hwc
= &event
->hw
;
50 /* test the event attr type check for PMU enumeration */
51 if (event
->attr
.type
!= event
->pmu
->type
)
54 /* update the hw_perf_event struct with config data */
55 hwc
->conf
= event
->attr
.config
;
60 /* start perf counter */
61 static void amdgpu_perf_start(struct perf_event
*event
, int flags
)
63 struct hw_perf_event
*hwc
= &event
->hw
;
64 struct amdgpu_pmu_entry
*pe
= container_of(event
->pmu
,
65 struct amdgpu_pmu_entry
,
68 if (WARN_ON_ONCE(!(hwc
->state
& PERF_HES_STOPPED
)))
71 WARN_ON_ONCE(!(hwc
->state
& PERF_HES_UPTODATE
));
74 switch (pe
->pmu_perf_type
) {
75 case PERF_TYPE_AMDGPU_DF
:
76 if (!(flags
& PERF_EF_RELOAD
))
77 pe
->adev
->df
.funcs
->pmc_start(pe
->adev
, hwc
->conf
, 1);
79 pe
->adev
->df
.funcs
->pmc_start(pe
->adev
, hwc
->conf
, 0);
85 perf_event_update_userpage(event
);
89 /* read perf counter */
90 static void amdgpu_perf_read(struct perf_event
*event
)
92 struct hw_perf_event
*hwc
= &event
->hw
;
93 struct amdgpu_pmu_entry
*pe
= container_of(event
->pmu
,
94 struct amdgpu_pmu_entry
,
100 prev
= local64_read(&hwc
->prev_count
);
102 switch (pe
->pmu_perf_type
) {
103 case PERF_TYPE_AMDGPU_DF
:
104 pe
->adev
->df
.funcs
->pmc_get_count(pe
->adev
, hwc
->conf
,
111 } while (local64_cmpxchg(&hwc
->prev_count
, prev
, count
) != prev
);
113 local64_add(count
- prev
, &event
->count
);
116 /* stop perf counter */
117 static void amdgpu_perf_stop(struct perf_event
*event
, int flags
)
119 struct hw_perf_event
*hwc
= &event
->hw
;
120 struct amdgpu_pmu_entry
*pe
= container_of(event
->pmu
,
121 struct amdgpu_pmu_entry
,
124 if (hwc
->state
& PERF_HES_UPTODATE
)
127 switch (pe
->pmu_perf_type
) {
128 case PERF_TYPE_AMDGPU_DF
:
129 pe
->adev
->df
.funcs
->pmc_stop(pe
->adev
, hwc
->conf
, 0);
135 WARN_ON_ONCE(hwc
->state
& PERF_HES_STOPPED
);
136 hwc
->state
|= PERF_HES_STOPPED
;
138 if (hwc
->state
& PERF_HES_UPTODATE
)
141 amdgpu_perf_read(event
);
142 hwc
->state
|= PERF_HES_UPTODATE
;
145 /* add perf counter */
146 static int amdgpu_perf_add(struct perf_event
*event
, int flags
)
148 struct hw_perf_event
*hwc
= &event
->hw
;
151 struct amdgpu_pmu_entry
*pe
= container_of(event
->pmu
,
152 struct amdgpu_pmu_entry
,
155 event
->hw
.state
= PERF_HES_UPTODATE
| PERF_HES_STOPPED
;
157 switch (pe
->pmu_perf_type
) {
158 case PERF_TYPE_AMDGPU_DF
:
159 retval
= pe
->adev
->df
.funcs
->pmc_start(pe
->adev
, hwc
->conf
, 1);
168 if (flags
& PERF_EF_START
)
169 amdgpu_perf_start(event
, PERF_EF_RELOAD
);
175 /* delete perf counter */
176 static void amdgpu_perf_del(struct perf_event
*event
, int flags
)
178 struct hw_perf_event
*hwc
= &event
->hw
;
179 struct amdgpu_pmu_entry
*pe
= container_of(event
->pmu
,
180 struct amdgpu_pmu_entry
,
183 amdgpu_perf_stop(event
, PERF_EF_UPDATE
);
185 switch (pe
->pmu_perf_type
) {
186 case PERF_TYPE_AMDGPU_DF
:
187 pe
->adev
->df
.funcs
->pmc_stop(pe
->adev
, hwc
->conf
, 1);
193 perf_event_update_userpage(event
);
198 /* init pmu tracking per pmu type */
199 static int init_pmu_by_type(struct amdgpu_device
*adev
,
200 const struct attribute_group
*attr_groups
[],
201 char *pmu_type_name
, char *pmu_file_prefix
,
202 unsigned int pmu_perf_type
,
203 unsigned int num_counters
)
205 char pmu_name
[PMU_NAME_SIZE
];
206 struct amdgpu_pmu_entry
*pmu_entry
;
209 pmu_entry
= kzalloc(sizeof(struct amdgpu_pmu_entry
), GFP_KERNEL
);
214 pmu_entry
->adev
= adev
;
215 pmu_entry
->pmu
= (struct pmu
){
216 .event_init
= amdgpu_perf_event_init
,
217 .add
= amdgpu_perf_add
,
218 .del
= amdgpu_perf_del
,
219 .start
= amdgpu_perf_start
,
220 .stop
= amdgpu_perf_stop
,
221 .read
= amdgpu_perf_read
,
222 .task_ctx_nr
= perf_invalid_context
,
225 pmu_entry
->pmu
.attr_groups
= attr_groups
;
226 pmu_entry
->pmu_perf_type
= pmu_perf_type
;
227 snprintf(pmu_name
, PMU_NAME_SIZE
, "%s_%d",
228 pmu_file_prefix
, adev
->ddev
->primary
->index
);
230 ret
= perf_pmu_register(&pmu_entry
->pmu
, pmu_name
, -1);
234 pr_warn("Error initializing AMDGPU %s PMUs.\n", pmu_type_name
);
238 pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n",
239 pmu_type_name
, num_counters
);
241 list_add_tail(&pmu_entry
->entry
, &amdgpu_pmu_list
);
246 /* init amdgpu_pmu */
247 int amdgpu_pmu_init(struct amdgpu_device
*adev
)
251 switch (adev
->asic_type
) {
254 ret
= init_pmu_by_type(adev
, df_v3_6_attr_groups
,
255 "DF", "amdgpu_df", PERF_TYPE_AMDGPU_DF
,
256 DF_V3_6_MAX_COUNTERS
);
258 /* other pmu types go here*/
268 /* destroy all pmu data associated with target device */
269 void amdgpu_pmu_fini(struct amdgpu_device
*adev
)
271 struct amdgpu_pmu_entry
*pe
, *temp
;
273 list_for_each_entry_safe(pe
, temp
, &amdgpu_pmu_list
, entry
) {
274 if (pe
->adev
== adev
) {
275 list_del(&pe
->entry
);
276 perf_pmu_unregister(&pe
->pmu
);