xref: /linux/tools/perf/util/drm_pmu.c (revision 9e906a9dead17d81d6c2687f65e159231d0e3286)
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 
perf_pmu__is_drm(const struct perf_pmu * pmu)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 
evsel__is_drm(const struct evsel * evsel)68 bool evsel__is_drm(const struct evsel *evsel)
69 {
70 	return perf_pmu__is_drm(evsel->pmu);
71 }
72 
add_drm_pmu(struct list_head * pmus,char * line,size_t line_len)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 
starts_with(const char * str,const char * prefix)132 static bool starts_with(const char *str, const char *prefix)
133 {
134 	return !strncmp(prefix, str, strlen(prefix));
135 }
136 
add_event(struct drm_pmu_event ** events,int * num_events,const char * line,enum drm_pmu_unit unit,const char * desc)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 
read_drm_pmus_cb(void * args,int fdinfo_dir_fd,const char * fd_name)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 
drm_pmu__exit(struct perf_pmu * pmu)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 
drm_pmu__have_event(const struct perf_pmu * pmu,const char * name)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 
drm_pmu__for_each_event(const struct perf_pmu * pmu,void * state,pmu_event_callback cb)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 
drm_pmu__num_events(const struct perf_pmu * pmu)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 
drm_pmu__index_for_event(const struct drm_pmu * drm,const char * name)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 
drm_pmu__config_term(const struct drm_pmu * drm,struct perf_event_attr * attr,struct parse_events_term * term,struct parse_events_error * err)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 
drm_pmu__config_terms(const struct perf_pmu * pmu,struct perf_event_attr * attr,struct parse_events_terms * terms,struct parse_events_error * err)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 
drm_pmu__check_alias(const struct perf_pmu * pmu,struct parse_events_terms * terms,struct perf_pmu_info * info,struct parse_events_error * err)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 
for_each_drm_fdinfo_in_dir(int (* cb)(void * args,int fdinfo_dir_fd,const char * fd_name),void * args,int proc_dir,const char * pid_name,struct minor_info * minors)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 
for_each_drm_fdinfo(bool skip_all_duplicates,int (* cb)(void * args,int fdinfo_dir_fd,const char * fd_name),void * args)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 
perf_pmus__read_drm_pmus(struct list_head * pmus)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 
evsel__drm_pmu_open(struct evsel * evsel,struct perf_thread_map * threads,int start_cpu_map_idx,int end_cpu_map_idx)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 
read_count_and_apply_unit(const char * count_and_unit,enum drm_pmu_unit unit)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 
read_drm_event(int fdinfo_dir_fd,const char * fd_name,const char * match,enum drm_pmu_unit unit)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 
read_drm_event_cb(void * vargs,int fdinfo_dir_fd,const char * fd_name)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 
drm_pmu__read_system_wide(struct drm_pmu * drm,struct evsel * evsel)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 
drm_pmu__read_for_pid(struct drm_pmu * drm,struct evsel * evsel,int pid)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 
evsel__drm_pmu_read(struct evsel * evsel,int cpu_map_idx,int thread)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