1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 #include "drm_pmu.h" 3 #include "counts.h" 4 #include "cpumap.h" 5 #include "debug.h" 6 #include "evsel.h" 7 #include "pmu.h" 8 #include <perf/threadmap.h> 9 #include <api/fs/fs.h> 10 #include <api/io.h> 11 #include <ctype.h> 12 #include <dirent.h> 13 #include <fcntl.h> 14 #include <unistd.h> 15 #include <linux/unistd.h> 16 #include <linux/kcmp.h> 17 #include <linux/zalloc.h> 18 #include <sys/stat.h> 19 #include <sys/syscall.h> 20 #include <sys/sysmacros.h> 21 #include <sys/types.h> 22 23 enum drm_pmu_unit { 24 DRM_PMU_UNIT_BYTES, 25 DRM_PMU_UNIT_CAPACITY, 26 DRM_PMU_UNIT_CYCLES, 27 DRM_PMU_UNIT_HZ, 28 DRM_PMU_UNIT_NS, 29 30 DRM_PMU_UNIT_MAX, 31 }; 32 33 struct drm_pmu_event { 34 const char *name; 35 const char *desc; 36 enum drm_pmu_unit unit; 37 }; 38 39 struct drm_pmu { 40 struct perf_pmu pmu; 41 struct drm_pmu_event *events; 42 int num_events; 43 }; 44 45 static const char * const drm_pmu_unit_strs[DRM_PMU_UNIT_MAX] = { 46 "bytes", 47 "capacity", 48 "cycles", 49 "hz", 50 "ns", 51 }; 52 53 static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] = { 54 "1bytes", 55 "1capacity", 56 "1cycles", 57 "1hz", 58 "1ns", 59 }; 60 61 bool perf_pmu__is_drm(const struct perf_pmu *pmu) 62 { 63 return pmu && pmu->type >= PERF_PMU_TYPE_DRM_START && 64 pmu->type <= PERF_PMU_TYPE_DRM_END; 65 } 66 67 bool evsel__is_drm(const struct evsel *evsel) 68 { 69 return perf_pmu__is_drm(evsel->pmu); 70 } 71 72 static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, size_t line_len) 73 { 74 struct drm_pmu *drm; 75 struct perf_pmu *pmu; 76 const char *name; 77 __u32 max_drm_pmu_type = 0, type; 78 int i = 12; 79 80 if (line[line_len - 1] == '\n') 81 line[line_len - 1] = '\0'; 82 while (isspace(line[i])) 83 i++; 84 85 line[--i] = '_'; 86 line[--i] = 'm'; 87 line[--i] = 'r'; 88 line[--i] = 'd'; 89 name = &line[i]; 90 91 list_for_each_entry(pmu, pmus, list) { 92 if (!perf_pmu__is_drm(pmu)) 93 continue; 94 if (pmu->type > max_drm_pmu_type) 95 max_drm_pmu_type = pmu->type; 96 if (!strcmp(pmu->name, name)) { 97 /* PMU already exists. */ 98 return NULL; 99 } 100 } 101 102 if (max_drm_pmu_type != 0) 103 type = max_drm_pmu_type + 1; 104 else 105 type = PERF_PMU_TYPE_DRM_START; 106 107 if (type > PERF_PMU_TYPE_DRM_END) { 108 zfree(&drm); 109 pr_err("Unable to encode DRM PMU type for %s\n", name); 110 return NULL; 111 } 112 113 drm = zalloc(sizeof(*drm)); 114 if (!drm) 115 return NULL; 116 117 if (perf_pmu__init(&drm->pmu, type, name) != 0) { 118 perf_pmu__delete(&drm->pmu); 119 return NULL; 120 } 121 122 drm->pmu.cpus = perf_cpu_map__new("0"); 123 if (!drm->pmu.cpus) { 124 perf_pmu__delete(&drm->pmu); 125 return NULL; 126 } 127 return drm; 128 } 129 130 131 static bool starts_with(const char *str, const char *prefix) 132 { 133 return !strncmp(prefix, str, strlen(prefix)); 134 } 135 136 static int add_event(struct drm_pmu_event **events, int *num_events, 137 const char *line, enum drm_pmu_unit unit, const char *desc) 138 { 139 const char *colon = strchr(line, ':'); 140 struct drm_pmu_event *tmp; 141 142 if (!colon) 143 return -EINVAL; 144 145 tmp = reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_event)); 146 if (!tmp) 147 return -ENOMEM; 148 tmp[*num_events].unit = unit; 149 tmp[*num_events].desc = desc; 150 tmp[*num_events].name = strndup(line, colon - line); 151 if (!tmp[*num_events].name) 152 return -ENOMEM; 153 (*num_events)++; 154 *events = tmp; 155 return 0; 156 } 157 158 static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name) 159 { 160 struct list_head *pmus = args; 161 char buf[640]; 162 struct io io; 163 char *line = NULL; 164 size_t line_len; 165 struct drm_pmu *drm = NULL; 166 struct drm_pmu_event *events = NULL; 167 int num_events = 0; 168 169 io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); 170 if (io.fd == -1) { 171 /* Failed to open file, ignore. */ 172 return 0; 173 } 174 175 while (io__getline(&io, &line, &line_len) > 0) { 176 if (starts_with(line, "drm-driver:")) { 177 drm = add_drm_pmu(pmus, line, line_len); 178 if (!drm) 179 break; 180 continue; 181 } 182 /* 183 * Note the string matching below is alphabetical, with more 184 * specific matches appearing before less specific. 185 */ 186 if (starts_with(line, "drm-active-")) { 187 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 188 "Total memory active in one or more engines"); 189 continue; 190 } 191 if (starts_with(line, "drm-cycles-")) { 192 add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES, 193 "Busy cycles"); 194 continue; 195 } 196 if (starts_with(line, "drm-engine-capacity-")) { 197 add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY, 198 "Engine capacity"); 199 continue; 200 } 201 if (starts_with(line, "drm-engine-")) { 202 add_event(&events, &num_events, line, DRM_PMU_UNIT_NS, 203 "Utilization in ns"); 204 continue; 205 } 206 if (starts_with(line, "drm-maxfreq-")) { 207 add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ, 208 "Maximum frequency"); 209 continue; 210 } 211 if (starts_with(line, "drm-purgeable-")) { 212 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 213 "Size of resident and purgeable memory buffers"); 214 continue; 215 } 216 if (starts_with(line, "drm-resident-")) { 217 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 218 "Size of resident memory buffers"); 219 continue; 220 } 221 if (starts_with(line, "drm-shared-")) { 222 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 223 "Size of shared memory buffers"); 224 continue; 225 } 226 if (starts_with(line, "drm-total-cycles-")) { 227 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 228 "Total busy cycles"); 229 continue; 230 } 231 if (starts_with(line, "drm-total-")) { 232 add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, 233 "Size of shared and private memory"); 234 continue; 235 } 236 if (verbose > 1 && starts_with(line, "drm-") && 237 !starts_with(line, "drm-client-id:") && 238 !starts_with(line, "drm-pdev:")) 239 pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line); 240 } 241 if (drm) { 242 drm->events = events; 243 drm->num_events = num_events; 244 list_add_tail(&drm->pmu.list, pmus); 245 } 246 free(line); 247 if (io.fd != -1) 248 close(io.fd); 249 return 0; 250 } 251 252 void drm_pmu__exit(struct perf_pmu *pmu) 253 { 254 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 255 256 free(drm->events); 257 } 258 259 bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name) 260 { 261 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 262 263 if (!starts_with(name, "drm-")) 264 return false; 265 266 for (int i = 0; i < drm->num_events; i++) { 267 if (!strcasecmp(drm->events[i].name, name)) 268 return true; 269 } 270 return false; 271 } 272 273 int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_event_callback cb) 274 { 275 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 276 277 for (int i = 0; i < drm->num_events; i++) { 278 char encoding_buf[128]; 279 struct pmu_event_info info = { 280 .pmu = pmu, 281 .name = drm->events[i].name, 282 .alias = NULL, 283 .scale_unit = drm_pmu_scale_unit_strs[drm->events[i].unit], 284 .desc = drm->events[i].desc, 285 .long_desc = NULL, 286 .encoding_desc = encoding_buf, 287 .topic = "drm", 288 .pmu_name = pmu->name, 289 .event_type_desc = "DRM event", 290 }; 291 int ret; 292 293 snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%x/", pmu->name, i); 294 295 ret = cb(state, &info); 296 if (ret) 297 return ret; 298 } 299 return 0; 300 } 301 302 size_t drm_pmu__num_events(const struct perf_pmu *pmu) 303 { 304 const struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 305 306 return drm->num_events; 307 } 308 309 static int drm_pmu__index_for_event(const struct drm_pmu *drm, const char *name) 310 { 311 for (int i = 0; i < drm->num_events; i++) { 312 if (!strcmp(drm->events[i].name, name)) 313 return i; 314 } 315 return -1; 316 } 317 318 static int drm_pmu__config_term(const struct drm_pmu *drm, 319 struct perf_event_attr *attr, 320 struct parse_events_term *term, 321 struct parse_events_error *err) 322 { 323 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { 324 int i = drm_pmu__index_for_event(drm, term->config); 325 326 if (i >= 0) { 327 attr->config = i; 328 return 0; 329 } 330 } 331 if (err) { 332 char *err_str; 333 334 parse_events_error__handle(err, term->err_val, 335 asprintf(&err_str, 336 "unexpected drm event term (%s) %s", 337 parse_events__term_type_str(term->type_term), 338 term->config) < 0 339 ? strdup("unexpected drm event term") 340 : err_str, 341 NULL); 342 } 343 return -EINVAL; 344 } 345 346 int drm_pmu__config_terms(const struct perf_pmu *pmu, 347 struct perf_event_attr *attr, 348 struct parse_events_terms *terms, 349 struct parse_events_error *err) 350 { 351 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 352 struct parse_events_term *term; 353 354 list_for_each_entry(term, &terms->terms, list) { 355 if (drm_pmu__config_term(drm, attr, term, err)) 356 return -EINVAL; 357 } 358 359 return 0; 360 } 361 362 int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_terms *terms, 363 struct perf_pmu_info *info, struct parse_events_error *err) 364 { 365 struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); 366 struct parse_events_term *term = 367 list_first_entry(&terms->terms, struct parse_events_term, list); 368 369 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { 370 int i = drm_pmu__index_for_event(drm, term->config); 371 372 if (i >= 0) { 373 info->unit = drm_pmu_unit_strs[drm->events[i].unit]; 374 info->scale = 1; 375 return 0; 376 } 377 } 378 if (err) { 379 char *err_str; 380 381 parse_events_error__handle(err, term->err_val, 382 asprintf(&err_str, 383 "unexpected drm event term (%s) %s", 384 parse_events__term_type_str(term->type_term), 385 term->config) < 0 386 ? strdup("unexpected drm event term") 387 : err_str, 388 NULL); 389 } 390 return -EINVAL; 391 } 392 393 struct minor_info { 394 unsigned int *minors; 395 int minors_num, minors_len; 396 }; 397 398 static int for_each_drm_fdinfo_in_dir(int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), 399 void *args, int proc_dir, const char *pid_name, 400 struct minor_info *minors) 401 { 402 char buf[256]; 403 DIR *fd_dir; 404 struct dirent *fd_entry; 405 int fd_dir_fd, fdinfo_dir_fd = -1; 406 407 408 scnprintf(buf, sizeof(buf), "%s/fd", pid_name); 409 fd_dir_fd = openat(proc_dir, buf, O_DIRECTORY); 410 if (fd_dir_fd == -1) 411 return 0; /* Presumably lost race to open. */ 412 fd_dir = fdopendir(fd_dir_fd); 413 if (!fd_dir) { 414 close(fd_dir_fd); 415 return -ENOMEM; 416 } 417 while ((fd_entry = readdir(fd_dir)) != NULL) { 418 struct stat stat; 419 unsigned int minor; 420 bool is_dup = false; 421 int ret; 422 423 if (fd_entry->d_type != DT_LNK) 424 continue; 425 426 if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) != 0) 427 continue; 428 429 if ((stat.st_mode & S_IFMT) != S_IFCHR || major(stat.st_rdev) != 226) 430 continue; 431 432 minor = minor(stat.st_rdev); 433 for (int i = 0; i < minors->minors_num; i++) { 434 if (minor(stat.st_rdev) == minors->minors[i]) { 435 is_dup = true; 436 break; 437 } 438 } 439 if (is_dup) 440 continue; 441 442 if (minors->minors_num == minors->minors_len) { 443 unsigned int *tmp = reallocarray(minors->minors, minors->minors_len + 4, 444 sizeof(unsigned int)); 445 446 if (tmp) { 447 minors->minors = tmp; 448 minors->minors_len += 4; 449 } 450 } 451 minors->minors[minors->minors_num++] = minor; 452 if (fdinfo_dir_fd == -1) { 453 /* Open fdinfo dir if we have a DRM fd. */ 454 scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name); 455 fdinfo_dir_fd = openat(proc_dir, buf, O_DIRECTORY); 456 if (fdinfo_dir_fd == -1) 457 continue; 458 } 459 ret = cb(args, fdinfo_dir_fd, fd_entry->d_name); 460 if (ret) 461 return ret; 462 } 463 if (fdinfo_dir_fd != -1) 464 close(fdinfo_dir_fd); 465 closedir(fd_dir); 466 return 0; 467 } 468 469 static int for_each_drm_fdinfo(bool skip_all_duplicates, 470 int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), 471 void *args) 472 { 473 DIR *proc_dir; 474 struct dirent *proc_entry; 475 int ret; 476 /* 477 * minors maintains an array of DRM minor device numbers seen for a pid, 478 * or for all pids if skip_all_duplicates is true, so that duplicates 479 * are ignored. 480 */ 481 struct minor_info minors = { 482 .minors = NULL, 483 .minors_num = 0, 484 .minors_len = 0, 485 }; 486 487 proc_dir = opendir(procfs__mountpoint()); 488 if (!proc_dir) 489 return 0; 490 491 /* Walk through the /proc directory. */ 492 while ((proc_entry = readdir(proc_dir)) != NULL) { 493 if (proc_entry->d_type != DT_DIR || 494 !isdigit(proc_entry->d_name[0])) 495 continue; 496 if (!skip_all_duplicates) { 497 /* Reset the seen minor numbers for each pid. */ 498 minors.minors_num = 0; 499 } 500 ret = for_each_drm_fdinfo_in_dir(cb, args, 501 dirfd(proc_dir), proc_entry->d_name, 502 &minors); 503 if (ret) 504 break; 505 } 506 free(minors.minors); 507 closedir(proc_dir); 508 return ret; 509 } 510 511 int perf_pmus__read_drm_pmus(struct list_head *pmus) 512 { 513 return for_each_drm_fdinfo(/*skip_all_duplicates=*/true, read_drm_pmus_cb, pmus); 514 } 515 516 int evsel__drm_pmu_open(struct evsel *evsel, 517 struct perf_thread_map *threads, 518 int start_cpu_map_idx, int end_cpu_map_idx) 519 { 520 (void)evsel; 521 (void)threads; 522 (void)start_cpu_map_idx; 523 (void)end_cpu_map_idx; 524 return 0; 525 } 526 527 static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum drm_pmu_unit unit) 528 { 529 char *unit_ptr = NULL; 530 uint64_t count = strtoul(count_and_unit, &unit_ptr, 10); 531 532 if (!unit_ptr) 533 return 0; 534 535 while (isblank(*unit_ptr)) 536 unit_ptr++; 537 538 switch (unit) { 539 case DRM_PMU_UNIT_BYTES: 540 if (*unit_ptr == '\0') 541 assert(count == 0); /* Generally undocumented, happens for 0. */ 542 else if (!strcmp(unit_ptr, "KiB")) 543 count *= 1024; 544 else if (!strcmp(unit_ptr, "MiB")) 545 count *= 1024 * 1024; 546 else 547 pr_err("Unexpected bytes unit '%s'\n", unit_ptr); 548 break; 549 case DRM_PMU_UNIT_CAPACITY: 550 /* No units expected. */ 551 break; 552 case DRM_PMU_UNIT_CYCLES: 553 /* No units expected. */ 554 break; 555 case DRM_PMU_UNIT_HZ: 556 if (!strcmp(unit_ptr, "Hz")) 557 count *= 1; 558 else if (!strcmp(unit_ptr, "KHz")) 559 count *= 1000; 560 else if (!strcmp(unit_ptr, "MHz")) 561 count *= 1000000; 562 else 563 pr_err("Unexpected hz unit '%s'\n", unit_ptr); 564 break; 565 case DRM_PMU_UNIT_NS: 566 /* Only unit ns expected. */ 567 break; 568 case DRM_PMU_UNIT_MAX: 569 default: 570 break; 571 } 572 return count; 573 } 574 575 static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name, 576 const char *match, enum drm_pmu_unit unit) 577 { 578 char buf[640]; 579 struct io io; 580 char *line = NULL; 581 size_t line_len; 582 uint64_t count = 0; 583 584 io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); 585 if (io.fd == -1) { 586 /* Failed to open file, ignore. */ 587 return 0; 588 } 589 while (io__getline(&io, &line, &line_len) > 0) { 590 size_t i = strlen(match); 591 592 if (strncmp(line, match, i)) 593 continue; 594 if (line[i] != ':') 595 continue; 596 while (isblank(line[++i])) 597 ; 598 if (line[line_len - 1] == '\n') 599 line[line_len - 1] = '\0'; 600 count = read_count_and_apply_unit(&line[i], unit); 601 break; 602 } 603 free(line); 604 close(io.fd); 605 return count; 606 } 607 608 struct read_drm_event_cb_args { 609 const char *match; 610 uint64_t count; 611 enum drm_pmu_unit unit; 612 }; 613 614 static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *fd_name) 615 { 616 struct read_drm_event_cb_args *args = vargs; 617 618 args->count += read_drm_event(fdinfo_dir_fd, fd_name, args->match, args->unit); 619 return 0; 620 } 621 622 static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evsel *evsel) 623 { 624 struct read_drm_event_cb_args args = { 625 .count = 0, 626 .match = drm->events[evsel->core.attr.config].name, 627 .unit = drm->events[evsel->core.attr.config].unit, 628 }; 629 630 for_each_drm_fdinfo(/*skip_all_duplicates=*/false, read_drm_event_cb, &args); 631 return args.count; 632 } 633 634 static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *evsel, int pid) 635 { 636 struct read_drm_event_cb_args args = { 637 .count = 0, 638 .match = drm->events[evsel->core.attr.config].name, 639 .unit = drm->events[evsel->core.attr.config].unit, 640 }; 641 struct minor_info minors = { 642 .minors = NULL, 643 .minors_num = 0, 644 .minors_len = 0, 645 }; 646 int proc_dir = open(procfs__mountpoint(), O_DIRECTORY); 647 char pid_name[12]; 648 int ret; 649 650 if (proc_dir < 0) 651 return 0; 652 653 snprintf(pid_name, sizeof(pid_name), "%d", pid); 654 ret = for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pid_name, &minors); 655 free(minors.minors); 656 close(proc_dir); 657 return ret == 0 ? args.count : 0; 658 } 659 660 int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) 661 { 662 struct drm_pmu *drm = container_of(evsel->pmu, struct drm_pmu, pmu); 663 struct perf_counts_values *count, *old_count = NULL; 664 int pid = perf_thread_map__pid(evsel->core.threads, thread); 665 uint64_t counter; 666 667 if (pid != -1) 668 counter = drm_pmu__read_for_pid(drm, evsel, pid); 669 else 670 counter = drm_pmu__read_system_wide(drm, evsel); 671 672 if (evsel->prev_raw_counts) 673 old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); 674 675 count = perf_counts(evsel->counts, cpu_map_idx, thread); 676 if (old_count) { 677 count->val = old_count->val + counter; 678 count->run = old_count->run + 1; 679 count->ena = old_count->ena + 1; 680 } else { 681 count->val = counter; 682 count->run++; 683 count->ena++; 684 } 685 return 0; 686 } 687