xref: /linux/tools/perf/util/drm_pmu.c (revision ec714e371f22f716a04e6ecb2a24988c92b26911)
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 			goto close_fdinfo;
462 	}
463 
464 close_fdinfo:
465 	if (fdinfo_dir_fd != -1)
466 		close(fdinfo_dir_fd);
467 	closedir(fd_dir);
468 	return 0;
469 }
470 
for_each_drm_fdinfo(bool skip_all_duplicates,int (* cb)(void * args,int fdinfo_dir_fd,const char * fd_name),void * args)471 static int for_each_drm_fdinfo(bool skip_all_duplicates,
472 			       int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name),
473 			       void *args)
474 {
475 	DIR *proc_dir;
476 	struct dirent *proc_entry;
477 	int ret;
478 	/*
479 	 * minors maintains an array of DRM minor device numbers seen for a pid,
480 	 * or for all pids if skip_all_duplicates is true, so that duplicates
481 	 * are ignored.
482 	 */
483 	struct minor_info minors = {
484 		.minors = NULL,
485 		.minors_num = 0,
486 		.minors_len = 0,
487 	};
488 
489 	proc_dir = opendir(procfs__mountpoint());
490 	if (!proc_dir)
491 		return 0;
492 
493 	/* Walk through the /proc directory. */
494 	while ((proc_entry = readdir(proc_dir)) != NULL) {
495 		if (proc_entry->d_type != DT_DIR ||
496 		    !isdigit(proc_entry->d_name[0]))
497 			continue;
498 		if (!skip_all_duplicates) {
499 			/* Reset the seen minor numbers for each pid. */
500 			minors.minors_num = 0;
501 		}
502 		ret = for_each_drm_fdinfo_in_dir(cb, args,
503 						 dirfd(proc_dir), proc_entry->d_name,
504 						 &minors);
505 		if (ret)
506 			break;
507 	}
508 	free(minors.minors);
509 	closedir(proc_dir);
510 	return ret;
511 }
512 
perf_pmus__read_drm_pmus(struct list_head * pmus)513 int perf_pmus__read_drm_pmus(struct list_head *pmus)
514 {
515 	return for_each_drm_fdinfo(/*skip_all_duplicates=*/true, read_drm_pmus_cb, pmus);
516 }
517 
evsel__drm_pmu_open(struct evsel * evsel,struct perf_thread_map * threads,int start_cpu_map_idx,int end_cpu_map_idx)518 int evsel__drm_pmu_open(struct evsel *evsel,
519 			struct perf_thread_map *threads,
520 			int start_cpu_map_idx, int end_cpu_map_idx)
521 {
522 	(void)evsel;
523 	(void)threads;
524 	(void)start_cpu_map_idx;
525 	(void)end_cpu_map_idx;
526 	return 0;
527 }
528 
read_count_and_apply_unit(const char * count_and_unit,enum drm_pmu_unit unit)529 static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum drm_pmu_unit unit)
530 {
531 	char *unit_ptr = NULL;
532 	uint64_t count = strtoul(count_and_unit, &unit_ptr, 10);
533 
534 	if (!unit_ptr)
535 		return 0;
536 
537 	while (isblank(*unit_ptr))
538 		unit_ptr++;
539 
540 	switch (unit) {
541 	case DRM_PMU_UNIT_BYTES:
542 		if (*unit_ptr == '\0')
543 			assert(count == 0); /* Generally undocumented, happens for 0. */
544 		else if (!strcmp(unit_ptr, "KiB"))
545 			count *= 1024;
546 		else if (!strcmp(unit_ptr, "MiB"))
547 			count *= 1024 * 1024;
548 		else
549 			pr_err("Unexpected bytes unit '%s'\n", unit_ptr);
550 		break;
551 	case DRM_PMU_UNIT_CAPACITY:
552 		/* No units expected. */
553 		break;
554 	case DRM_PMU_UNIT_CYCLES:
555 		/* No units expected. */
556 		break;
557 	case DRM_PMU_UNIT_HZ:
558 		if (!strcmp(unit_ptr, "Hz"))
559 			count *= 1;
560 		else if (!strcmp(unit_ptr, "KHz"))
561 			count *= 1000;
562 		else if (!strcmp(unit_ptr, "MHz"))
563 			count *= 1000000;
564 		else
565 			pr_err("Unexpected hz unit '%s'\n", unit_ptr);
566 		break;
567 	case DRM_PMU_UNIT_NS:
568 		/* Only unit ns expected. */
569 		break;
570 	case DRM_PMU_UNIT_MAX:
571 	default:
572 		break;
573 	}
574 	return count;
575 }
576 
read_drm_event(int fdinfo_dir_fd,const char * fd_name,const char * match,enum drm_pmu_unit unit)577 static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name,
578 			       const char *match, enum drm_pmu_unit unit)
579 {
580 	char buf[640];
581 	struct io io;
582 	char *line = NULL;
583 	size_t line_len;
584 	uint64_t count = 0;
585 
586 	io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf));
587 	if (io.fd == -1) {
588 		/* Failed to open file, ignore. */
589 		return 0;
590 	}
591 	while (io__getline(&io, &line, &line_len) > 0) {
592 		size_t i = strlen(match);
593 
594 		if (strncmp(line, match, i))
595 			continue;
596 		if (line[i] != ':')
597 			continue;
598 		while (isblank(line[++i]))
599 			;
600 		if (line[line_len - 1] == '\n')
601 			line[line_len - 1] = '\0';
602 		count = read_count_and_apply_unit(&line[i], unit);
603 		break;
604 	}
605 	free(line);
606 	close(io.fd);
607 	return count;
608 }
609 
610 struct read_drm_event_cb_args {
611 	const char *match;
612 	uint64_t count;
613 	enum drm_pmu_unit unit;
614 };
615 
read_drm_event_cb(void * vargs,int fdinfo_dir_fd,const char * fd_name)616 static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *fd_name)
617 {
618 	struct read_drm_event_cb_args *args = vargs;
619 
620 	args->count += read_drm_event(fdinfo_dir_fd, fd_name, args->match, args->unit);
621 	return 0;
622 }
623 
drm_pmu__read_system_wide(struct drm_pmu * drm,struct evsel * evsel)624 static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evsel *evsel)
625 {
626 	struct read_drm_event_cb_args args = {
627 		.count = 0,
628 		.match = drm->events[evsel->core.attr.config].name,
629 		.unit = drm->events[evsel->core.attr.config].unit,
630 	};
631 
632 	for_each_drm_fdinfo(/*skip_all_duplicates=*/false, read_drm_event_cb, &args);
633 	return args.count;
634 }
635 
drm_pmu__read_for_pid(struct drm_pmu * drm,struct evsel * evsel,int pid)636 static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *evsel, int pid)
637 {
638 	struct read_drm_event_cb_args args = {
639 		.count = 0,
640 		.match = drm->events[evsel->core.attr.config].name,
641 		.unit = drm->events[evsel->core.attr.config].unit,
642 	};
643 	struct minor_info minors = {
644 		.minors = NULL,
645 		.minors_num = 0,
646 		.minors_len = 0,
647 	};
648 	int proc_dir = open(procfs__mountpoint(), O_DIRECTORY);
649 	char pid_name[12];
650 	int ret;
651 
652 	if (proc_dir < 0)
653 		return 0;
654 
655 	snprintf(pid_name, sizeof(pid_name), "%d", pid);
656 	ret = for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pid_name, &minors);
657 	free(minors.minors);
658 	close(proc_dir);
659 	return ret == 0 ? args.count : 0;
660 }
661 
evsel__drm_pmu_read(struct evsel * evsel,int cpu_map_idx,int thread)662 int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
663 {
664 	struct drm_pmu *drm = container_of(evsel->pmu, struct drm_pmu, pmu);
665 	struct perf_counts_values *count, *old_count = NULL;
666 	int pid = perf_thread_map__pid(evsel->core.threads, thread);
667 	uint64_t counter;
668 
669 	if (pid != -1)
670 		counter = drm_pmu__read_for_pid(drm, evsel, pid);
671 	else
672 		counter = drm_pmu__read_system_wide(drm, evsel);
673 
674 	if (evsel->prev_raw_counts)
675 		old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
676 
677 	count = perf_counts(evsel->counts, cpu_map_idx, thread);
678 	if (old_count) {
679 		count->val = old_count->val + counter;
680 		count->run = old_count->run + 1;
681 		count->ena = old_count->ena + 1;
682 	} else {
683 		count->val = counter;
684 		count->run++;
685 		count->ena++;
686 	}
687 	return 0;
688 }
689