| 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 |
|
| 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, "%s\n", amdgpu_pmu_attr->event_str);
|
| 70 |
|
| 71 | return sprintf(buf, "%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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 209 | static int amdgpu_perf_event_init(struct perf_event *event)
|
| 210 | {
|
| 211 | struct hw_perf_event *hwc = &event->hw;
|
| 212 |
|
| 213 |
|
| 214 | if (event->attr.type != event->pmu->type)
|
| 215 | return -ENOENT;
|
| 216 |
|
| 217 |
|
| 218 | hwc->config = event->attr.config;
|
| 219 | hwc->config_base = AMDGPU_PMU_PERF_TYPE_NONE;
|
| 220 |
|
| 221 | return 0;
|
| 222 | }
|
| 223 |
|
| 224 |
|
| 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 ,
|
| 249 | 1 );
|
| 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 |
|
| 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(&hwc->prev_count, &prev, count));
|
| 292 |
|
| 293 | local64_add(count - prev, &event->count);
|
| 294 | }
|
| 295 |
|
| 296 |
|
| 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 |
|
| 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 ,
|
| 362 | 1 );
|
| 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 |
|
| 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, 0,
|
| 438 | num_events, 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 = kzalloc_objs(**fmt_attr, config->num_formats);
|
| 450 |
|
| 451 | if (!(*fmt_attr))
|
| 452 | return -ENOMEM;
|
| 453 |
|
| 454 | fmt_attr_group->attrs = kzalloc_objs(*fmt_attr_group->attrs,
|
| 455 | config->num_formats + 1);
|
| 456 |
|
| 457 | if (!fmt_attr_group->attrs)
|
| 458 | goto err_fmt_attr_grp;
|
| 459 |
|
| 460 | *evt_attr = kzalloc_objs(**evt_attr, config->num_events);
|
| 461 |
|
| 462 | if (!(*evt_attr))
|
| 463 | goto err_evt_attr;
|
| 464 |
|
| 465 | evt_attr_group->attrs = kzalloc_objs(*evt_attr_group->attrs,
|
| 466 | config->num_events + 1);
|
| 467 |
|
| 468 | if (!evt_attr_group->attrs)
|
| 469 | goto err_evt_attr_grp;
|
| 470 |
|
| 471 | return 0;
|
| 472 | err_evt_attr_grp:
|
| 473 | kfree(*evt_attr);
|
| 474 | err_evt_attr:
|
| 475 | kfree(fmt_attr_group->attrs);
|
| 476 | err_fmt_attr_grp:
|
| 477 | kfree(*fmt_attr);
|
| 478 | return -ENOMEM;
|
| 479 | }
|
| 480 |
|
| 481 |
|
| 482 | static int init_pmu_entry_by_type_and_add(struct amdgpu_pmu_entry *pmu_entry,
|
| 483 | struct amdgpu_pmu_config *config)
|
| 484 | {
|
| 485 | const struct attribute_group *attr_groups[] = {
|
| 486 | &pmu_entry->fmt_attr_group,
|
| 487 | &pmu_entry->evt_attr_group,
|
| 488 | NULL
|
| 489 | };
|
| 490 | char pmu_name[PMU_NAME_SIZE];
|
| 491 | int ret = 0, total_num_events = 0;
|
| 492 |
|
| 493 | pmu_entry->pmu = (struct pmu){
|
| 494 | .event_init = amdgpu_perf_event_init,
|
| 495 | .add = amdgpu_perf_add,
|
| 496 | .del = amdgpu_perf_del,
|
| 497 | .start = amdgpu_perf_start,
|
| 498 | .stop = amdgpu_perf_stop,
|
| 499 | .read = amdgpu_perf_read,
|
| 500 | .task_ctx_nr = perf_invalid_context,
|
| 501 | };
|
| 502 |
|
| 503 | ret = amdgpu_pmu_alloc_pmu_attrs(&pmu_entry->fmt_attr_group,
|
| 504 | &pmu_entry->fmt_attr,
|
| 505 | &pmu_entry->evt_attr_group,
|
| 506 | &pmu_entry->evt_attr,
|
| 507 | config);
|
| 508 |
|
| 509 | if (ret)
|
| 510 | goto err_out;
|
| 511 |
|
| 512 | amdgpu_pmu_create_attrs(&pmu_entry->fmt_attr_group, pmu_entry->fmt_attr,
|
| 513 | config->formats, config->num_formats);
|
| 514 |
|
| 515 | if (pmu_entry->pmu_perf_type == AMDGPU_PMU_PERF_TYPE_ALL) {
|
| 516 | int i;
|
| 517 |
|
| 518 | for (i = 0; i < config->num_types; i++) {
|
| 519 | amdgpu_pmu_create_event_attrs_by_type(
|
| 520 | &pmu_entry->evt_attr_group,
|
| 521 | pmu_entry->evt_attr,
|
| 522 | config->events,
|
| 523 | total_num_events,
|
| 524 | total_num_events +
|
| 525 | config->types[i].num_of_type,
|
| 526 | config->types[i].type);
|
| 527 | total_num_events += config->types[i].num_of_type;
|
| 528 | }
|
| 529 | } else {
|
| 530 | amdgpu_pmu_create_attrs(&pmu_entry->evt_attr_group,
|
| 531 | pmu_entry->evt_attr,
|
| 532 | config->events, config->num_events);
|
| 533 | total_num_events = config->num_events;
|
| 534 | }
|
| 535 |
|
| 536 | pmu_entry->pmu.attr_groups = kmemdup(attr_groups, sizeof(attr_groups),
|
| 537 | GFP_KERNEL);
|
| 538 |
|
| 539 | if (!pmu_entry->pmu.attr_groups) {
|
| 540 | ret = -ENOMEM;
|
| 541 | goto err_attr_group;
|
| 542 | }
|
| 543 |
|
| 544 | snprintf(pmu_name, PMU_NAME_SIZE, "%s_%d", pmu_entry->pmu_file_prefix,
|
| 545 | adev_to_drm(pmu_entry->adev)->primary->index);
|
| 546 |
|
| 547 | ret = perf_pmu_register(&pmu_entry->pmu, pmu_name, -1);
|
| 548 |
|
| 549 | if (ret)
|
| 550 | goto err_register;
|
| 551 |
|
| 552 | if (pmu_entry->pmu_perf_type != AMDGPU_PMU_PERF_TYPE_ALL)
|
| 553 | pr_info("Detected AMDGPU %s Counters. # of Counters = %d.\n",
|
| 554 | pmu_entry->pmu_type_name, total_num_events);
|
| 555 | else
|
| 556 | pr_info("Detected AMDGPU %d Perf Events.\n", total_num_events);
|
| 557 |
|
| 558 |
|
| 559 | list_add_tail(&pmu_entry->entry, &amdgpu_pmu_list);
|
| 560 |
|
| 561 | return 0;
|
| 562 | err_register:
|
| 563 | kfree(pmu_entry->pmu.attr_groups);
|
| 564 | err_attr_group:
|
| 565 | kfree(pmu_entry->fmt_attr_group.attrs);
|
| 566 | kfree(pmu_entry->fmt_attr);
|
| 567 | kfree(pmu_entry->evt_attr_group.attrs);
|
| 568 | kfree(pmu_entry->evt_attr);
|
| 569 | err_out:
|
| 570 | pr_warn("Error initializing AMDGPU %s PMUs.\n",
|
| 571 | pmu_entry->pmu_type_name);
|
| 572 | return ret;
|
| 573 | }
|
| 574 |
|
| 575 |
|
| 576 | void amdgpu_pmu_fini(struct amdgpu_device *adev)
|
| 577 | {
|
| 578 | struct amdgpu_pmu_entry *pe, *temp;
|
| 579 |
|
| 580 | list_for_each_entry_safe(pe, temp, &amdgpu_pmu_list, entry) {
|
| 581 | if (pe->adev != adev)
|
| 582 | continue;
|
| 583 | list_del(&pe->entry);
|
| 584 | perf_pmu_unregister(&pe->pmu);
|
| 585 | kfree(pe->pmu.attr_groups);
|
| 586 | kfree(pe->fmt_attr_group.attrs);
|
| 587 | kfree(pe->fmt_attr);
|
| 588 | kfree(pe->evt_attr_group.attrs);
|
| 589 | kfree(pe->evt_attr);
|
| 590 | kfree(pe);
|
| 591 | }
|
| 592 | }
|
| 593 |
|
| 594 | static struct amdgpu_pmu_entry *create_pmu_entry(struct amdgpu_device *adev,
|
| 595 | unsigned int pmu_type,
|
| 596 | char *pmu_type_name,
|
| 597 | char *pmu_file_prefix)
|
| 598 | {
|
| 599 | struct amdgpu_pmu_entry *pmu_entry;
|
| 600 |
|
| 601 | pmu_entry = kzalloc_obj(struct amdgpu_pmu_entry);
|
| 602 |
|
| 603 | if (!pmu_entry)
|
| 604 | return pmu_entry;
|
| 605 |
|
| 606 | pmu_entry->adev = adev;
|
| 607 | pmu_entry->fmt_attr_group.name = "format";
|
| 608 | pmu_entry->fmt_attr_group.attrs = NULL;
|
| 609 | pmu_entry->evt_attr_group.name = "events";
|
| 610 | pmu_entry->evt_attr_group.attrs = NULL;
|
| 611 | pmu_entry->pmu_perf_type = pmu_type;
|
| 612 | pmu_entry->pmu_type_name = pmu_type_name;
|
| 613 | pmu_entry->pmu_file_prefix = pmu_file_prefix;
|
| 614 |
|
| 615 | return pmu_entry;
|
| 616 | }
|
| 617 |
|
| 618 |
|
| 619 | int amdgpu_pmu_init(struct amdgpu_device *adev)
|
| 620 | {
|
| 621 | int ret = 0;
|
| 622 | struct amdgpu_pmu_entry *pmu_entry, *pmu_entry_df;
|
| 623 |
|
| 624 | switch (adev->asic_type) {
|
| 625 | case CHIP_VEGA20:
|
| 626 | pmu_entry_df = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_DF,
|
| 627 | "DF", "amdgpu_df");
|
| 628 |
|
| 629 | if (!pmu_entry_df)
|
| 630 | return -ENOMEM;
|
| 631 |
|
| 632 | ret = init_pmu_entry_by_type_and_add(pmu_entry_df,
|
| 633 | &df_vega20_config);
|
| 634 |
|
| 635 | if (ret) {
|
| 636 | kfree(pmu_entry_df);
|
| 637 | return ret;
|
| 638 | }
|
| 639 |
|
| 640 | pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL,
|
| 641 | "", "amdgpu");
|
| 642 |
|
| 643 | if (!pmu_entry) {
|
| 644 | amdgpu_pmu_fini(adev);
|
| 645 | return -ENOMEM;
|
| 646 | }
|
| 647 |
|
| 648 | ret = init_pmu_entry_by_type_and_add(pmu_entry,
|
| 649 | &vega20_config);
|
| 650 |
|
| 651 | if (ret) {
|
| 652 | kfree(pmu_entry);
|
| 653 | amdgpu_pmu_fini(adev);
|
| 654 | return ret;
|
| 655 | }
|
| 656 |
|
| 657 | break;
|
| 658 | case CHIP_ARCTURUS:
|
| 659 | pmu_entry = create_pmu_entry(adev, AMDGPU_PMU_PERF_TYPE_ALL,
|
| 660 | "", "amdgpu");
|
| 661 | if (!pmu_entry)
|
| 662 | return -ENOMEM;
|
| 663 |
|
| 664 | ret = init_pmu_entry_by_type_and_add(pmu_entry,
|
| 665 | &arcturus_config);
|
| 666 |
|
| 667 | if (ret) {
|
| 668 | kfree(pmu_entry);
|
| 669 | return -ENOMEM;
|
| 670 | }
|
| 671 |
|
| 672 | break;
|
| 673 |
|
| 674 | default:
|
| 675 | return 0;
|
| 676 | }
|
| 677 |
|
| 678 | return ret;
|
| 679 | }
|