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