Merge branch 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / gpu / drm / amd / amdgpu / amdgpu_pmu.c
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  * Author: Jonathan Kim <jonathan.kim@amd.com>
23  *
24  */
25
26 #include <linux/perf_event.h>
27 #include <linux/init.h>
28 #include "amdgpu.h"
29 #include "amdgpu_pmu.h"
30 #include "df_v3_6.h"
31
32 #define PMU_NAME_SIZE 32
33
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;
38         struct pmu pmu;
39         unsigned int pmu_perf_type;
40 };
41
42 static LIST_HEAD(amdgpu_pmu_list);
43
44
45 /* initialize perf counter */
46 static int amdgpu_perf_event_init(struct perf_event *event)
47 {
48         struct hw_perf_event *hwc = &event->hw;
49
50         /* test the event attr type check for PMU enumeration */
51         if (event->attr.type != event->pmu->type)
52                 return -ENOENT;
53
54         /* update the hw_perf_event struct with config data */
55         hwc->config = event->attr.config;
56
57         return 0;
58 }
59
60 /* start perf counter */
61 static void amdgpu_perf_start(struct perf_event *event, int flags)
62 {
63         struct hw_perf_event *hwc = &event->hw;
64         struct amdgpu_pmu_entry *pe = container_of(event->pmu,
65                                                   struct amdgpu_pmu_entry,
66                                                   pmu);
67
68         if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
69                 return;
70
71         WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
72         hwc->state = 0;
73
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->config, 1);
78
79                 pe->adev->df.funcs->pmc_start(pe->adev, hwc->config, 0);
80                 break;
81         default:
82                 break;
83         }
84
85         perf_event_update_userpage(event);
86
87 }
88
89 /* read perf counter */
90 static void amdgpu_perf_read(struct perf_event *event)
91 {
92         struct hw_perf_event *hwc = &event->hw;
93         struct amdgpu_pmu_entry *pe = container_of(event->pmu,
94                                                   struct amdgpu_pmu_entry,
95                                                   pmu);
96
97         u64 count, prev;
98
99         do {
100                 prev = local64_read(&hwc->prev_count);
101
102                 switch (pe->pmu_perf_type) {
103                 case PERF_TYPE_AMDGPU_DF:
104                         pe->adev->df.funcs->pmc_get_count(pe->adev, hwc->config,
105                                                           &count);
106                         break;
107                 default:
108                         count = 0;
109                         break;
110                 }
111         } while (local64_cmpxchg(&hwc->prev_count, prev, count) != prev);
112
113         local64_add(count - prev, &event->count);
114 }
115
116 /* stop perf counter */
117 static void amdgpu_perf_stop(struct perf_event *event, int flags)
118 {
119         struct hw_perf_event *hwc = &event->hw;
120         struct amdgpu_pmu_entry *pe = container_of(event->pmu,
121                                                   struct amdgpu_pmu_entry,
122                                                   pmu);
123
124         if (hwc->state & PERF_HES_UPTODATE)
125                 return;
126
127         switch (pe->pmu_perf_type) {
128         case PERF_TYPE_AMDGPU_DF:
129                 pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, 0);
130                 break;
131         default:
132                 break;
133         }
134
135         WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
136         hwc->state |= PERF_HES_STOPPED;
137
138         if (hwc->state & PERF_HES_UPTODATE)
139                 return;
140
141         amdgpu_perf_read(event);
142         hwc->state |= PERF_HES_UPTODATE;
143 }
144
145 /* add perf counter  */
146 static int amdgpu_perf_add(struct perf_event *event, int flags)
147 {
148         struct hw_perf_event *hwc = &event->hw;
149         int retval;
150
151         struct amdgpu_pmu_entry *pe = container_of(event->pmu,
152                                                   struct amdgpu_pmu_entry,
153                                                   pmu);
154
155         event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
156
157         switch (pe->pmu_perf_type) {
158         case PERF_TYPE_AMDGPU_DF:
159                 retval = pe->adev->df.funcs->pmc_start(pe->adev,
160                                                        hwc->config, 1);
161                 break;
162         default:
163                 return 0;
164         }
165
166         if (retval)
167                 return retval;
168
169         if (flags & PERF_EF_START)
170                 amdgpu_perf_start(event, PERF_EF_RELOAD);
171
172         return retval;
173
174 }
175
176 /* delete perf counter  */
177 static void amdgpu_perf_del(struct perf_event *event, int flags)
178 {
179         struct hw_perf_event *hwc = &event->hw;
180         struct amdgpu_pmu_entry *pe = container_of(event->pmu,
181                                                   struct amdgpu_pmu_entry,
182                                                   pmu);
183
184         amdgpu_perf_stop(event, PERF_EF_UPDATE);
185
186         switch (pe->pmu_perf_type) {
187         case PERF_TYPE_AMDGPU_DF:
188                 pe->adev->df.funcs->pmc_stop(pe->adev, hwc->config, 1);
189                 break;
190         default:
191                 break;
192         }
193
194         perf_event_update_userpage(event);
195 }
196
197 /* vega20 pmus */
198
199 /* init pmu tracking per pmu type */
200 static int init_pmu_by_type(struct amdgpu_device *adev,
201                   const struct attribute_group *attr_groups[],
202                   char *pmu_type_name, char *pmu_file_prefix,
203                   unsigned int pmu_perf_type,
204                   unsigned int num_counters)
205 {
206         char pmu_name[PMU_NAME_SIZE];
207         struct amdgpu_pmu_entry *pmu_entry;
208         int ret = 0;
209
210         pmu_entry = kzalloc(sizeof(struct amdgpu_pmu_entry), GFP_KERNEL);
211
212         if (!pmu_entry)
213                 return -ENOMEM;
214
215         pmu_entry->adev = adev;
216         pmu_entry->pmu = (struct pmu){
217                 .event_init = amdgpu_perf_event_init,
218                 .add = amdgpu_perf_add,
219                 .del = amdgpu_perf_del,
220                 .start = amdgpu_perf_start,
221                 .stop = amdgpu_perf_stop,
222                 .read = amdgpu_perf_read,
223                 .task_ctx_nr = perf_invalid_context,
224         };
225
226         pmu_entry->pmu.attr_groups = attr_groups;
227         pmu_entry->pmu_perf_type = pmu_perf_type;
228         snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d",
229                                 pmu_file_prefix, adev->ddev->primary->index);
230
231         ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1);
232
233         if (ret) {
234                 kfree(pmu_entry);
235                 pr_warn("Error initializing AMDGPU %s PMUs.\n", pmu_type_name);
236                 return ret;
237         }
238
239         pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n",
240                         pmu_type_name, num_counters);
241
242         list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list);
243
244         return 0;
245 }
246
247 /* init amdgpu_pmu */
248 int amdgpu_pmu_init(struct amdgpu_device *adev)
249 {
250         int ret = 0;
251
252         switch (adev->asic_type) {
253         case CHIP_VEGA20:
254                 /* init df */
255                 ret = init_pmu_by_type(adev, df_v3_6_attr_groups,
256                                        "DF", "amdgpu_df", PERF_TYPE_AMDGPU_DF,
257                                        DF_V3_6_MAX_COUNTERS);
258
259                 /* other pmu types go here*/
260                 break;
261         default:
262                 return 0;
263         }
264
265         return 0;
266 }
267
268
269 /* destroy all pmu data associated with target device */
270 void amdgpu_pmu_fini(struct amdgpu_device *adev)
271 {
272         struct amdgpu_pmu_entry *pe, *temp;
273
274         list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) {
275                 if (pe->adev == adev) {
276                         list_del(&pe->entry);
277                         perf_pmu_unregister(&pe->pmu);
278                         kfree(pe);
279                 }
280         }
281 }