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